Plinth for VS Code
Catch the missing index in your editor, not in your incident channel.
Plinth reads your ORM queries as you write them, finds the ones that will slow down at production scale, and writes the exact change to fix them.
|
PostgreSQL |
MySQL |
| Prisma |
✓ |
✓ |
| Drizzle |
✓ |
✓ |
| TypeORM |
✓ |
✓ |
What it catches
- Unindexed filters and sorts: a filter plus sort with no composite index behind it.
- Foreign keys without an index: PostgreSQL never creates them; MySQL does only for real constraints.
- Unbounded list queries: no
take / limit, so the result grows with the table.
- Queries in loops: one round trip per item (it knows Prisma batches
findUnique).
The fix, in your ORM's own terms
| ORM |
Schema change |
Migration |
| Prisma |
@@index([status, createdAt]) in the model |
prisma/migrations/<timestamp>_add_…_index/migration.sql |
| Drizzle |
index("…").on(t.status, t.createdAt) in the table |
run drizzle-kit generate |
| TypeORM |
@Index("…", ["status", "createdAt"]) on the entity |
src/migrations/<timestamp>-Add…Index.ts (your migrations path) |
Index names are explicit and identical in the schema and the SQL, so your migration tool reports no drift. On PostgreSQL the SQL uses CREATE INDEX CONCURRENTLY; on MySQL, online DDL (ALGORITHM=INPLACE LOCK=NONE). TypeORM migrations build concurrently only when your DataSource sets migrationsTransactionMode: "each", because CONCURRENTLY cannot run inside a transaction.
Confidence and table sizes
Static code can't see how big your tables are, so every finding carries a confidence level. Raise it by declaring rough sizes in plinth.config.json next to your package.json. Keys can be model, entity or table names:
{ "tables": { "orders": { "rows": 5000000, "distinct": { "status": 4 } } } }
Or run Plinth: Copy table-size query and execute it on a replica or staging database. It reads planner statistics only (PostgreSQL pg_class/pg_stats, MySQL information_schema), never row contents, and returns that JSON ready to paste. Tables declared under 10k rows are left alone.
Plinth reads the database from your ORM config (Prisma provider, Drizzle pgTable/mysqlTable, TypeORM DataSource type). If it can't, set "dialect": "postgresql" or "mysql" in plinth.config.json.
Silencing a finding
// plinth-ignore-next-line unbounded-query
const all = await prisma.country.findMany();
Use Ignore … here from the lightbulb, or // plinth-ignore-file at the top of a file.
Settings
| Setting |
Default |
Description |
plinth.enable |
true |
Analyze as you type |
plinth.schemaPath |
auto |
Prisma only: schema.prisma or a prisma/schema/ folder |
plinth.minConfidence |
low |
Hide findings below this confidence |
Privacy
Everything runs locally. Your code and schema never leave your machine. Plinth collects no telemetry.