QueryTrace
Catches N+1 query loops, unbounded queries, and raw SQL injection risks in your ORM code while you type, before they take down production.
QueryTrace watches JavaScript, TypeScript, Python, and Ruby files for query patterns that look harmless in development but fall over at scale: a loop of findUnique calls that becomes 10,001 round trips, a findMany with no take that returns the whole table, a $queryRawUnsafe built from interpolated strings that hands attackers your database.
How it works
- Files are scanned automatically on open and as you type (debounced), and findings appear as squiggles in the editor and entries in the Problems panel (source
QueryTrace, code = rule id).
- Hover any finding for an explanation of why it is slow plus a rewrite, e.g. loop of findUnique -> single findMany with in: ids.
- The lightbulb offers quick fixes where a safe edit exists, plus per-line ignore and per-rule disable.
- Scan Workspace sweeps every supported file and fills the Query Issues tree view (Explorer sidebar), grouped rule -> file -> finding; click a finding to jump to it.
- The status bar shows
N query issues for the active file; click it to focus the Query Issues view.
Rules
| Rule id |
Language |
Default severity |
Catches |
n1-query-in-loop |
JS/TS |
Error |
Awaited ORM call (Prisma, TypeORM, Sequelize, Mongoose, Knex) inside for / for-of / while / forEach / map bodies |
aggregate-in-loop |
JS/TS |
Error |
count / aggregate / groupBy awaited inside a loop |
unbounded-query |
JS/TS |
Warning |
findMany / findAll / Model.find without take or limit in API route, controller, or handler files |
raw-sql-injection |
JS/TS |
Error |
$queryRawUnsafe, $executeRawUnsafe, .raw(...), sequelize.query(...), or .query(...) built from ${...} interpolation or string concatenation |
floating-query-promise |
JS/TS |
Warning |
Prisma or repository call whose promise is never awaited or used |
py-query-in-loop |
Python |
Error |
session.query, select, .execute, .objects.filter / .all inside for / while loops (SQLAlchemy, Django) |
py-objects-get-in-loop |
Python |
Error |
Model.objects.get or get_object_or_404 inside a loop |
py-missing-eager-load |
Python |
Info |
Iterating .all() while accessing relationship attributes; suggests joinedload / select_related |
rb-query-in-block |
Ruby |
Error |
Model.find / find_by / .where inside .each / .map blocks |
rb-association-in-loop |
Ruby |
Warning |
Association traversal on the block variable inside a loop; suggests .includes |
Example
N+1 in a loop:
const posts = await prisma.post.findMany();
for (const post of posts) {
const author = await prisma.user.findUnique({ where: { id: post.authorId } });
}
QueryTrace flags the inner call. Rewrite: loop of findUnique -> single findMany with in: ids
const posts = await prisma.post.findMany({ include: { author: true } });
Injection risk: $queryRawUnsafe( + interpolated SQL + ) -> $queryRaw tagged template, applied for you by the quick fix when the argument is a single template literal.
Quick fixes
- Add
take: 100 / limit: 100 / .limit(100) to unbounded queries
- Add an
include: {} skeleton to a flagged find call
- Convert
$queryRawUnsafe -> $queryRaw (and $executeRawUnsafe -> $executeRaw) tagged template
- Ignore this rule on this line (appends a trailing
querytrace-ignore marker comment; the line above also works)
- Disable the rule entirely via
queryTrace.disabledRules
Commands
All commands live under the QueryTrace category in the Command Palette.
- QueryTrace: Scan Workspace
- QueryTrace: Scan Current File
- QueryTrace: Show Query Issues View
- QueryTrace: Enable or Disable Scanning
- QueryTrace: Disable a Rule
- QueryTrace: Open Getting Started Walkthrough
Settings
{
"queryTrace.enabled": true,
"queryTrace.disabledRules": ["py-missing-eager-load"],
"queryTrace.severityOverrides": {
"unbounded-query": "error",
"rb-association-in-loop": "information"
}
}
queryTrace.enabled: master switch for all diagnostics.
queryTrace.disabledRules: array of rule ids to skip.
queryTrace.severityOverrides: map of rule id to error, warning, information, or hint.
Suppressing a single line
Add the marker querytrace-ignore in a comment on the flagged line or the line directly above it. The "Ignore this rule on this line" quick fix does this for you.
Getting started
A "Get Started with QueryTrace" walkthrough is available under Help -> Get Started, or run QueryTrace: Open Getting Started Walkthrough.
Requirements
Author
Kanaihya Kumar - kanaihyakmr@gmail.com
License
MIT