Skip to content

fix(fastify): per-query file tag, withRequestContext signature, and a first-party Fastify plugin#16

Merged
veksen merged 4 commits into
masterfrom
fix-sqlcommenter-fastify-concurrency
Jun 30, 2026
Merged

fix(fastify): per-query file tag, withRequestContext signature, and a first-party Fastify plugin#16
veksen merged 4 commits into
masterfrom
fix-sqlcommenter-fastify-concurrency

Conversation

@veksen

@veksen veksen commented Jun 30, 2026

Copy link
Copy Markdown
Member

Addresses the three issues reported in the Fastify + drizzle-orm/postgres-js dogfooding feedback (Site#3434). The investigation showed two of them are shared with the mikroorm/typeorm packages, so those are fixed too.

1. file tag clobbered / dropped under concurrency (drizzle) — bug

The build-time caller was stored in a WeakMap keyed by the drizzle session, which a drizzle() instance shares across every query. Two queries built before either executed collided on that one entry: the first to reach prepareQuery emitted both call sites joined (fileA;fileB) and deleted the entry; the second emitted no file tag. This breaks under any real concurrency (two in-flight HTTP requests, Promise.all).

Fix: then → execute → _prepare → session.prepareQuery runs synchronously, so each built query object is tagged with its own caller, published into a module variable only for that synchronous window. Concurrent queries can't interleave inside it, and it's robust to building/awaiting in different orders (an ordered queue would not be).

2. withRequestContext's signature rejected Fastify's done — DX

next: () => Promise<unknown> failed tsc (TS2345) when passed Fastify's done: (err?: Error) => void (and Express's next). Widened to () => unknown (next is only handed to als.run, return ignored). Also corrected the JSDoc wording.

3. Fastify integration docs + first-party plugin — DX

Two silent pitfalls when wiring by hand: a plain register() encapsulates the onRequest hook (applies to nothing, no error), and wrapping the handler misses queries in earlier onRequest/preHandler hooks.

  • New @query-doctor/sqlcommenter-<driver>/fastifysqlcommenterFastify: a global onRequest hook (via the skip-override symbol — no extra dependency) using request.routeOptions.url + request.method, with an optional context callback. fastify added as an optional peer dependency.
  • Fastify README section rewritten: recommends the plugin; for the manual path documents the fastify-plugin + onRequest + ordering requirements and routerPathrouteOptions.url (Fastify v5).

Verification

  • Reproduced the concurrency bug, then confirmed the fix across select / insert / update / delete / findMany / findFirst / execute, sequential + Promise.all + out-of-order build/await. New regression tests fail on the old code, pass on the new.
  • Signature: tsc confirms the new form compiles and the old one fails with the exact reported TS2345.
  • Plugin verified against a real Fastify v5 app — auth-phase (onRequest/preHandler) and handler queries all get route/method/file; a plain encapsulated register is silently untagged (negative control). The manual fastify-plugin path was also validated end-to-end.
  • All packages build (dual ESM/CJS) and pass: drizzle 19, mikroorm 30, typeorm 22.

Notes

  • No version bumps included — drizzle/mikroorm/typeorm would each warrant a minor (new /fastify entry; drizzle also the bug fix). Happy to add.
  • package-lock.json changes are only the fastify devDep tree (no fastify-plugin — that's user-installed, referenced only in the manual README example).

🤖 Generated with Claude Code

veksen and others added 4 commits June 30, 2026 14:02
The build-time caller was stored in a WeakMap keyed by the drizzle session,
which is shared across every query on a `drizzle()` instance. Two queries
built before either executed collided on that single entry: the first to
reach `prepareQuery` emitted both call sites joined and deleted the entry,
the second emitted no `file` tag at all. This dropped/mis-attributed the
tag under any real concurrency (two in-flight requests, `Promise.all`).

`then -> execute -> _prepare -> session.prepareQuery` runs synchronously, so
instead tag each built query object with its own caller and publish it only
for that synchronous window. Concurrent queries can't interleave inside it,
so each reads exactly its own caller — and it's robust to building and
awaiting queries in different orders, which an ordered queue would not be.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…llbacks

`next: () => Promise<unknown>` rejected Fastify's `done: (err?: Error) => void`
(and Express's `next`) with TS2345, since `void` isn't assignable to
`Promise<unknown>`. `next` is only handed to `als.run`, whose return value is
ignored, so widen it to `() => unknown`. Also corrects the JSDoc, which said
the context carries "route and controller" — it's `route` plus optional
`method`/`controller` and arbitrary keys.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… docs

Wiring sqlcommenter into Fastify by hand has two silent pitfalls: a plain
`register()` encapsulates the `onRequest` hook so it never applies to routes
in the parent scope (no tags, no error), and wrapping the route handler misses
queries issued in earlier `onRequest`/`preHandler` hooks (e.g. an auth plugin's
session lookup).

Add `@query-doctor/sqlcommenter-<driver>/fastify` exporting `sqlcommenterFastify`:
a global `onRequest` hook (via the `skip-override` symbol, so no extra
dependency) using `request.routeOptions.url` + `request.method`, with an
optional `context` callback for extra fields. Rewrite the Fastify README
section to recommend the plugin and, for the manual path, document the
`fastify-plugin` + `onRequest` + registration-ordering requirements and the
`routerPath` -> `routeOptions.url` change for Fastify v5.

Adds `fastify` as an optional peer dependency (and a dev dependency for tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- @query-doctor/sqlcommenter-drizzle: 0.3.0 -> 0.4.0 (file-tag fix + /fastify)
- @query-doctor/sqlcommenter-mikroorm: 0.2.0 -> 0.3.0 (/fastify)
- @query-doctor/sqlcommenter-typeorm: 0.2.0 -> 0.3.0 (/fastify)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@veksen veksen merged commit cdefab9 into master Jun 30, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant