Changelog
Changelog
0.3.0
One way for each kind of app, with no profiler code in your server: the Vite plugin for React Router, a preload for any other Node server. Also MySQL, MariaDB and SQLite, one profiler for several processes, SQL laid out over lines, themes, and Clear.
Breaking changes, and how to upgrade from 0.2
- Node 22.19+ or 24.5+. The preload gives your app’s imports of the profiler its real code
through Node’s synchronous module hooks, which cover
require()too. Before 22.19 and 24.5, Node ignores what such a hook adds onimportand throws on it inrequire(), so there the preload says so and stays off. Node 20, out of support since April 2026, is no longer supported. - React Router: the Vite plugin alone.
queryProfiler()invite.config.tsnow profiles each request, puts the bar on every page and serves/_profiler. RemoveprofilerMiddleware()from your root route (and the.servermodule that created it), and<ProfilerToolbar />if you used it; React Router 7 no longer needsfuture.v8_middlewarefor the profiler.query-profiler/react-router,/react-router/routeand/react-router/toolbarare gone. - Fastify, and any Node server: the preload. Remove
app.register(profilerPlugin)and its import fromquery-profiler/fastify, and add--import query-profiler/registerto your dev command. The profiler moves to a port of its own:http://localhost:4983/_profiler.query-profiler/fastifyis gone, and the package no longer depends onfastify-plugin. - No
--conditions=developmentin a dev command any more: the preload adds it. Leaving it does no harm.
New
- Any Node server, with no code in it: Express, Fastify, Koa, Hapi, NestJS, Hono. Add
--import query-profiler/registerto the dev command (node --watch,tsx watch, nodemon, the Nest CLI, orNODE_OPTIONS). The preload profiles each request from Node’shttp.server.request.startdiagnostics channel, tags each response withX-Debug-Token(exposed to CORS), and serves the profiler on a port of its own: 4983, orQUERY_PROFILER_PORT. It opens that port once the app listens, waits a moment for a restarting app to let go of it, then takes the next free one.QUERY_PROFILER_LOG=1prints one line per request. - One profiler for several processes: Node’s
cluster, PM2 in cluster mode, or two apps at once. The first process serves it and the others send it their profiles (POST /_profiler/api/profiles, JSON only and never from a browser page); when it stops, another one takes the port over. QUERY_PROFILER_HOSTpicks where the profiler listens:0.0.0.0reaches it from outside a container with its port published. It still answers only requests addressed to a local host, so the network cannot read it.- MySQL, MariaDB and SQLite, through Prisma’s own adapters (
@prisma/adapter-mariadband@prisma/adapter-better-sqlite3), tested in CI on MySQL 8.4, MariaDB 11.4 and SQLite with the same checks as PostgreSQL. Secrets are hidden in their?parameters and backtick-quoted columns too, and “Copy runnable SQL” fills the?in order. Raw SQL is recorded through Prisma’s$queryRaw; through your own MySQL or SQLite driver it is not. - SQL laid out over lines. Opening a query shows its SQL as it would be written by hand: one
clause per line, a long list one item per line, a long condition one
ANDper line, subqueries indented, with keywords, strings and parameters in colour. “Copy runnable SQL” copies it that way. Only whitespace moves, never inside strings or quoted names; SQL with a line comment or a dollar-quoted body stays as written. - Themes. The bar and the profiler page are dark by default, in the website’s colours, with a
light theme and Auto, which follows the system. A switch in both picks it, and the browser
keeps the choice. A page can set the starting theme with
data-themeon the element. - Clear, in the profiler page and in the bar’s recent requests, removes every stored request. The
stream announces it (
event: clear), so every open tab empties its lists; the bar keeps the page it is on. - A page’s calls hang from it on one line, a branch and a dot each, in the bar and in the profiler page’s list, instead of a box each.
- HTTP/2 servers (Fastify with
http2: true, Hono on an HTTP/2 server) are profiled too, on Node 22.17+ and 24.2+. Their responses carry noX-Debug-Token. - The preload says why it is off instead of staying silent: a
NODE_ENVother thandevelopment, or a Node older than 22.19 or 24.5. Production, test runs andQUERY_PROFILER_DISABLED=1stay silent. - Development only, without a bundler: the docs show how to keep
query-profilera dev dependency for a server run with plainnode, or bundled with tsup, esbuild, Rollup or webpack. CI checks that the examples run with the package not installed. - A troubleshooting page in the docs, for when requests or queries do not show up.
- The profiler’s API answers
HEADlikeGET, with no body.
Fixed
- Compiled code run without
--enable-source-maps(tscandnode --watch dist/server.js) shows each query’s line in its source: the profiler reads the app file’s source map itself. - The line of code behind each query is found through a bundle’s source maps when Node runs with
--enable-source-maps, even if a framework formats stack traces itself. - An app run with tsx gets the right line of code for each query again; the line was mapped twice through the same source map.
- A server bundled with the profiler in development serves
/_profiler: the core finds its UI files from the app too, as a bundler moves it out of its package. - In React Router 8, a client navigation home becomes the new page in the bar: React Router 8 loads
/from/_.data, which the bar and the profiles took for a path of its own (/_). - A tab that was hidden while the profiler was cleared, or while the dev server restarted, takes the server’s lists when it comes back instead of merging them with its own, so gone requests do not come back.
0.2.1
- The package now links to its website, https://query-profiler.dev, with the docs and a live demo of the bar and the profiler page.
0.2.0
- Fastify 4.26 and later.
app.register(profilerPlugin)fromquery-profiler/fastifyprofiles each request, tags each response withX-Debug-Token(exposed to CORS) and serves/_profiler. Without a bundler, install the package as a dependency and run the dev server with--conditions=development; plainnodeloads the entry that does nothing. createHandler()serves the bar and the profiler page’s code itself (/_profiler/ui.js), so/_profilerworks on servers without Vite.- The profiler also runs when
NODE_ENVis not set, as servers without a bundler leave it, so--conditions=developmentalone turns it on there.NODE_ENV=productionorteststill keep it off. withProfiler(),profilePrisma(),profilePool()andprofileAdapter()return what they are given, untouched, outside development, like the middleware and the plugin: even if the real code were loaded in production, Prisma’s SQL would carry no stamp and nothing would be wrapped.- The terminal line from
formatProfile()says1 query,1 operationand1 warningin the singular.
0.1.2
- The bar and the profiler page showed nothing when
profilerMiddleware()was called withoutonProfile, as the README sets it up: the profile was never stored. Now every profile is stored, with or withoutonProfile. - A tab in the background no longer holds a connection to the dev server: the bar and the profiler page stop listening for new requests while their tab is hidden, and catch up when it shows again. Over HTTP/1.1 browsers allow six connections per host, so many open tabs used to stall the app.
0.1.1
- Secrets no longer show in SQL parameters. A parameter is hidden when it equals a value the
Prisma operation passed under a secret-looking key (
password,token,apiKey…), or when the SQL compares it with or writes it to a column named like one, raw SQL included.
0.1.0
First release.
- Captures every PostgreSQL statement sent through Prisma 7.1+ or
pg, linked to the Prisma operation and the line of code behind it, per request. - N+1, slow and duplicate warnings; time the database was busy.
- React Router 7.9+ and 8: one profile per request, and the calls a page makes (fetchers, navigations, actions) hung under it.
- A bar at the bottom of every page and a full profiler page at
/_profiler, with stats. - A Vite plugin that serves both in development and leaves the profiler out of builds.