Skip to content

Latest commit

 

History

History
132 lines (95 loc) · 4.42 KB

File metadata and controls

132 lines (95 loc) · 4.42 KB

@query-doctor/sqlcommenter-drizzle

Drizzle sqlcommenter support for drizzle >= 0.34.0 (including 1.0.0 beta).

Only tested on Postgres, but theoretically it should be compatible with all clients supported by drizzle.

Emits the following fields into the query:

name included by default? description
db_driver Yes The driver used to connect to the database. (Drizzle)
file Yes The file that the query was executed in.
func_name Yes, if named The function/method that built the query. Omitted if anonymous.
route No The route that the query was executed in.
method No The http method for the request that the query was executed in.
anything else No Any other information that the user wants to add to the query.

It also emits the trace context, if available.

Installation

npm install @query-doctor/sqlcommenter-drizzle
pnpm add @query-doctor/sqlcommenter-drizzle

Usage

Simply wrap your drizzle instance with the patchDrizzle function.

Before:

// db/drizle.ts
import { drizzle } from "drizzle-orm/postgres-js";

const db = drizzle(process.env.DATABASE_URL);

After:

// db/drizle.ts
import { drizzle } from "drizzle-orm/postgres-js";
import { patchDrizzle } from "@query-doctor/sqlcommenter-drizzle";

const db = patchDrizzle(drizzle(process.env.DATABASE_URL));

Emitting route information

To include route information in the comments, patching Drizzle by itself is not enough. You need to use the withRequestContext function to pass along relevant information to the query comments.

You can add any arbitrary information to the request context aside from route, method and controller.

Here are some examples of how to use it with different frameworks:

Express

import { withRequestContext } from "@query-doctor/sqlcommenter-drizzle/http";

app.use((req, res, next) => {
  withRequestContext({ route: req.route.path, method: req.method }, next);
});

Hono

import { withRequestContext } from "@query-doctor/sqlcommenter-drizzle/http";
import { routePath } from "hono/route";

app.use((c, next) => {
  withRequestContext({ route: routePath(c), method: c.req.method }, next);
});

Fastify

The recommended way is the first-party plugin, which wires everything correctly:

import { sqlcommenterFastify } from "@query-doctor/sqlcommenter-drizzle/fastify";

// Register it BEFORE any plugin whose hooks issue queries (e.g. auth), so the context
// is already open when those hooks run.
await app.register(sqlcommenterFastify);
await app.register(authPlugin);

It hooks onRequest, so it tags queries from the entire request lifecycle — including queries issued in other plugins' onRequest/preHandler hooks — not just the route handler. Pass context to add extra fields:

await app.register(sqlcommenterFastify, {
  context: (request) => ({ controller: "items" }),
});
Doing it manually

If you'd rather wire it yourself with withRequestContext, two things are easy to get wrong:

  • Register the hook globally with fastify-plugin. A plain app.register(plugin) encapsulates the onRequest hook, so it silently does not apply to routes registered in the parent scope — you get no route/method tags and no error.
  • Hook onRequest (not the route handler), registered before any plugin whose hooks issue queries (e.g. an auth plugin that resolves a session in its own onRequest/preHandler). Wrapping only the handler misses those earlier queries.
import fp from "fastify-plugin";
import { withRequestContext } from "@query-doctor/sqlcommenter-drizzle/http";

const sqlcommenter = fp((app, _opts, done) => {
  app.addHook("onRequest", (request, _reply, next) => {
    withRequestContext(
      {
        // `routerPath` was removed in Fastify v5; `routeOptions.url` is the matched route pattern.
        route: request.routeOptions?.url ?? request.url,
        method: request.method,
      },
      next
    );
  });
  done();
});

await app.register(sqlcommenter); // before auth, etc.