Middleware
Registering middleware
The same pipe can run for the whole app, for one group, or for a single route. Where you attach it decides its scope; a couple of options decide its order and let routes opt out.
#Scopes
| Scope | Type | Description |
|---|---|---|
| Global | app / kernel | Runs for every event. Set on the app (stone.middleware) or mark the middleware global. |
| Group | controller / children | In a controller's or group's middleware array; runs for its routes. |
| Route | per route | In a route's middleware array; runs for that route only. |
import { StoneApp, Middleware } from '@stone-js/core'
// Global: runs for every event. 'global: true' registers it kernel-wide.
@Middleware({ global: true, priority: 10 })
export class RequestId { /* ... */ }import { defineStoneApp } from '@stone-js/core'
import { requestId } from './middleware/requestId'
export const App = defineStoneApp(
{ name: 'tasks', middleware: [requestId] }, // global middleware
[/* blueprints */]
)#Per route and per group
@EventHandler('/tasks', { middleware: [requireAuth()] }) // whole controller
export class TaskController {
@Post('/', { middleware: [validate({ body: NewTask })] }) // one route, in addition
create (event) { /* ... */ }
}#Order, priority and opting out
- Order: global first, then group, then route; within a list, top to bottom.
- Priority: give a middleware a
priorityto move it earlier or later among its peers. - Alias: register a middleware under an
aliasand refer to it by name where that reads better. - Opt out: a route can drop an inherited middleware with
excludeMiddleware.
@Get('/public', { excludeMiddleware: [requireAuth] }) // skip the group's guard here
public () { /* no auth */ }| Option | Type | Default | Description |
|---|---|---|---|
| layer | 'app' | 'kernel' | 'router' | 'app' | Which pipeline it belongs to. Only the router layer runs with a route matched. |
| global | boolean | false | Register as kernel-wide middleware. The same thing as layer: kernel. |
| priority | number | · | Execution order among peers (lower runs earlier). |
| alias | string | string[] | · | Name(s) to reference the middleware by. |
| params | unknown[] | · | Arguments passed to the middleware. |
#When the middleware needs the route
Kernel middleware runs before a route has been matched, so it cannot read one. A middleware whose whole job is to read what a route declares, a permission, a resource, a contract, needs the layer that runs after matching:
@Middleware({ layer: 'router', priority: 5 })
export class ReadsTheRoute {
handle (event: IncomingHttpEvent, next: NextMiddleware) {
// A route has been matched here, and only here.
const declared = event.getRoute()?.getOption('resource')
return next(event)
}
}#Two layers: kernel and adapter
Almost all middleware is kernel middleware: it runs on the normalised IncomingEvent, inside the per-event container, everything on this page so far. There is also a rarer adapter middleware that runs at the integration edge, on the platform's raw cause and response, before the kernel builds the event.
import { defineAdapterMiddleware } from '@stone-js/core'
// Runs on the raw platform context, outside the kernel. For platform-level
// concerns only (raw timing, low-level headers); use kernel middleware otherwise.
export const rawTiming = defineAdapterMiddleware((context, next) => next(context))