Skip to content

SQLCommenter for TypeORM

import { Tabs, TabItem } from “@astrojs/starlight/components”;

@query-doctor/sqlcommenter-typeorm wraps your TypeORM DataSource so every SQL query carries a structured SQLCommenter comment with contextual metadata. Query Doctor reads these comments to connect queries back to the code and request that produced them.

  • Node.js >= 24.8.0
  • TypeORM >= 0.3.0
  • ESM project ("type": "module" in your package.json)
```sh npm install @query-doctor/sqlcommenter-typeorm ``` ```sh pnpm add @query-doctor/sqlcommenter-typeorm ```

Wrap your TypeORM DataSource with patchTypeORM:

import { DataSource } from "typeorm";
import { patchTypeORM } from "@query-doctor/sqlcommenter-typeorm";
const dataSource = patchTypeORM(
new DataSource({
type: "postgres",
url: process.env.DATABASE_URL,
}),
);

Every query executed through dataSource now includes a SQL comment with:

TagIncluded by defaultDescription
db_driverYesIdentifies TypeORM as the driver
fileYesSource file that triggered the query
func_nameWhen namedEnclosing function/method — edit-stable, omitted if anonymous
routeNoHTTP route (requires framework middleware)
methodNoHTTP request method (requires framework middleware)
Trace contextAutomaticOpenTelemetry trace/span IDs when a tracer is active
Custom fieldsNoAny arbitrary key/value metadata you provide

To capture route and method, use the withRequestContext function from the /http export. You can also pass any arbitrary key/value metadata.

import { withRequestContext } from "@query-doctor/sqlcommenter-typeorm/http";
app.use((req, res, next) => {
withRequestContext({ route: req.route.path, method: req.method }, next);
});
import { withRequestContext } from "@query-doctor/sqlcommenter-typeorm/http";
import { routePath } from "hono/route";
app.use((c, next) => {
withRequestContext({ route: routePath(c), method: c.req.method }, next);
});

The simplest way is the first-party plugin, which registers a global onRequest hook covering the whole request lifecycle:

import { sqlcommenterFastify } from "@query-doctor/sqlcommenter-typeorm/fastify";
// Register before any plugin whose hooks issue queries (e.g. auth).
await app.register(sqlcommenterFastify);

To wire it by hand instead, register the hook globally with fastify-plugin — a plain register() encapsulates the hook and it silently tags nothing:

import fp from "fastify-plugin";
import { withRequestContext } from "@query-doctor/sqlcommenter-typeorm/http";
const sqlcommenter = fp((app, _opts, done) => {
app.addHook("onRequest", (request, _reply, next) => {
// `routerPath` was removed in Fastify v5; `routeOptions.url` is the matched route pattern.
withRequestContext(
{
route: request.routeOptions?.url ?? request.url,
method: request.method,
},
next,
);
});
done();
});
await app.register(sqlcommenter);
import { withRequestContext } from "@query-doctor/sqlcommenter-typeorm/http";
@Injectable()
export class SqlcommenterMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
withRequestContext({ route: req.path, method: req.method }, next);
}
}