React Router
React Router 7.9+ or 8 in framework mode, with Prisma 7 and a driver adapter. Two files, nothing
in your routes, and nothing to remove before deploying. A complete app is in
examples/react-router.
npm i -D query-profilerIt is a dev dependency: the build gets none of it.
1. Add the Vite plugin
Section titled “1. Add the Vite plugin”Put it before reactRouter():
import { reactRouter } from "@react-router/dev/vite";import { queryProfiler } from "query-profiler/vite";import { defineConfig } from "vite";
export default defineConfig({ plugins: [queryProfiler({ log: true }), reactRouter()],});| Option | Default | What it does |
|---|---|---|
base |
/_profiler |
Where the profiler’s page and API live. |
log |
false |
Print one line per request in the terminal. |
2. Profile the Prisma client
Section titled “2. Profile the Prisma client”import { PrismaPg } from "@prisma/adapter-pg";import { profilePrisma, withProfiler } from "query-profiler/prisma";import { PrismaClient } from "./generated/prisma/client";
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
export const prisma = profilePrisma(new PrismaClient(withProfiler({ adapter })));The Prisma guide explains both calls, raw SQL and types.
Run your dev server and open any page: the bar is at the bottom, and
/_profiler has the full view.
How it fits in React Router
Section titled “How it fits in React Router”Nothing goes in your routes, and React Router 7 needs no future.v8_middleware:
- In development, the plugin adds a middleware to the Vite dev server, right before React Router’s.
It opens a profile for each request and runs the rest of it inside, so every loader, action and
query lands in it. It tags each response with
X-Debug-Token, and with aServer-Timingentry that carries the same token. - The bar comes in with React Router’s client entry (yours,
app/entry.client.tsx, or its default one), added by the plugin in development only. It reads its page’s token from the page’sServer-Timingheader before the app hydrates, so the fetchers a page runs on mount already hang under it. - In a build, the plugin swaps every
query-profilerimport for its no-op entry, and adds nothing to the client.
A custom server that runs Vite in middleware mode, as React Router’s Express template does, gets the same: the plugin’s middleware runs inside Vite’s.