Installation
Requirements
Section titled “Requirements”| What | Supported |
|---|---|
| Prisma ORM | 7.1 or later, with a driver adapter. Not 6; 8 is planned |
| Database | PostgreSQL, MySQL, MariaDB and SQLite, through Prisma’s own adapters |
| Node | 22.19+ or 24.5+ |
| Raw SQL | Prisma’s $queryRaw on any of them; pg (node-postgres), optional |
| Framework | React Router 7.9+ and 8 in framework mode (also Remix), with Vite 6+ |
| Framework | Any Node server: Express, Fastify, Koa, Hapi, NestJS, Hono |
See compatibility for what is coming.
Install
Section titled “Install”npm i -D query-profilerFrameworks and drivers are optional peer dependencies, so installing it never pulls in React, Vite or a server framework: it uses the ones your app already has.
Development only, by construction
Section titled “Development only, by construction”Every entry that runs inside your app has a development build with the real code and a
default one that does nothing, with the same API. How it stays out of production depends on how
you ship the server:
| How you ship | What leaves the profiler out |
|---|---|
| A Vite build (React Router) | The Vite plugin puts the empty entries in the build: production never needs it |
A Node server run with plain node |
Only the dev command loads the preload; the app imports Prisma’s entry if it is there |
| A bundled server (tsup, esbuild, Rollup, webpack) | The bundler puts the empty entries in the bundle (see below) |
There is no if (import.meta.env.DEV) around each call. In development, the real code comes from the
development condition: the Vite plugin and the preload add it, and a bundler adds it with its
conditions option.
A Node server without a bundler
Section titled “A Node server without a bundler”The server’s code needs nothing: the preload in the dev command profiles each
request. The Prisma client imports query-profiler/prisma only if it is installed, so a production
install (npm ci --omit=dev) leaves the package out and the app runs without it:
const profiler = await import("query-profiler/prisma").catch(() => undefined);
export const prisma: Omit<PrismaClient, "$on"> = profiler ? profiler.profilePrisma(new PrismaClient(profiler.withProfiler({ adapter }))) : new PrismaClient({ adapter });The examples in examples/express and examples/fastify are set up this way.
A bundled server
Section titled “A bundled server”With plain imports, a production bundle gets the empty entries, and the package can be a dev
dependency. tsup bundles dev dependencies by default; with esbuild, Rollup or webpack, do not mark
query-profiler as external. Bundle for development with the development condition (esbuild’s
conditions: ["development"]), and run that bundle with the preload:
node --import query-profiler/register dist/server.js. Importing
formatProfile from query-profiler itself keeps the core in the bundle, inert: leave it to the
development build.
Next: set it up in React Router or a Node server.