Skip to main content

framework integrations overview

Every framework needs the same three things wired up, catching the typed error, reading status off it, and writing the scrubbed JSON body. Only the hook for doing so differs.

The shared pattern

@gentleduck/error has no framework adapters of its own. It's deliberately zero-dependency, so there's nothing to import for Express or NestJS specifically. Every integration is the same three-step wiring, expressed through whichever hook that framework gives you for "run this when a handler throws":

Loading diagram...

What differs is only where that hook lives and what object it hands you to write the response with.

Choosing your integration

FrameworkHookResponse objectNotes
NestJS@Catch() + ExceptionFilterUnderlying platform Response (Express or Fastify)Use err.statusCode, which exists specifically for Nest's base filter compatibility
Express4-arg (err, req, res, next) middleware, registered lastresExpress 4 needs a manual try/catch + next(err) in async handlers; Express 5 forwards rejections automatically
Fastifyapp.setErrorHandler(...)replyOne handler for the whole app, no ordering to get right
Honoapp.onError((err, c) => ...)Built Response via c.json(...)Same hook works unmodified across every Hono runtime target (Node, Bun, Deno, Workers)
Next.jsManual try/catchNextResponse.json(...) (Route Handlers) or a returned discriminated value (Server Actions)The only framework here with no centralized hook, see why in its own page

All five runnable in full under examples/.

What stays the same everywhere

  • The check: err instanceof AppError (or asError(err, FALLBACK_CODE) when you want a uniform shape for both typed and untyped errors in one step. See Wrapping unknown errors).
  • The status: err.status (or err.statusCode, an identical value present for NestJS).
  • The body: err.toJSON(), already secret-scrubbed. See Secret redaction.
  • The client: whichever framework produced it, the response is the same plain JSON. No instanceof survives the wire. See Browser and isomorphic usage for how to consume it from a fetch call.

Nothing about the kit itself changes between frameworks. If you're adding a sixth framework not listed here, this three-step pattern is the whole of what you need to replicate: find that framework's equivalent of "run this when a handler throws or a promise it returned rejects," and wire the same three lines into it.