Middlewares and filters
A middleware runs before the handler: to authenticate, check a permission, resolve a tenant, rate-limit. A response filter runs after it, to transform the returned value.
Middlewares
A middleware is a function of the request context. It returns nothing to let the request continue, or an error (or a response) to stop it:
Any other value is a bug, answered with a 500. A thrown error stops the pipeline like a returned one.
defineMiddleware() only adds types; a plain function (ctx) => … is a middleware too.
Where to attach them
For a request, they run in this order:
- the global middlewares added by plugins (
@dotpress/authreads the token there); - the global middlewares of
createApp(); - the middlewares of the groups, outermost first;
- the middlewares of the route, in order.
Then the request is validated, and the handler runs.
Middlewares see the raw request
Middlewares run before validation (see Request pipeline): ctx.req.params, query and body hold the values received, not yet parsed by the schemas. A middleware that reads them checks them itself.
Middlewares that guarantee a value
A middleware can set a context key and guarantee it to the handlers. Declare the key optional with module augmentation, and the guarantee with the type parameter of defineMiddleware():
In the routes using requireWorkspace, directly or through a group, ctx.workspace is a Workspace. See End-to-end typing.
Documenting the security
A middleware that checks credentials can declare the security scheme it accepts. The OpenAPI document then marks the routes using it as secured:
requireAuth and requireRole() of @dotpress/auth declare bearerAuth.
Native middlewares of your server
Middlewares of the ecosystem of your server (helmet, compression, a Fastify plugin, a Hono middleware) still work: register them through the beforeRoutes option of the adapter. They run before Dotpress, and do not know about the route.
Prefer Dotpress middlewares for the logic of your API: they run on every adapter, see the matched route and the typed context, and can be tested in memory. See Adapters.
Response filters
A response filter receives the value returned by a handler, and returns the value to send. Use it to wrap every response in an envelope, or to add metadata:
Filters can be async. They run after the response schema has validated the value, and only for JSON results: errors, text(), stream(), file() and the other raw responses are sent as they are.
Before and after the handler
A middleware only runs before the handler. To measure a request, log it, or run it in a context (tracing, request-scoped storage), write a plugin with the onResponse or wrapRequest hook. See Logging and error hooks.
Middlewares now run before validation, and can be attached to groups. Their return value is checked: nothing, an error or a response. The Express hooks useBeforeRoutes and useAfterRoutes moved to the options of the Express adapter (beforeRoutes, afterRoutes).