patterntypescriptModerate
Middleware Composition: Build pipelines with explicit ordering and typed context
Viewed 0 times
middlewarepipelineorderingcontextauthcompositiontyped
Problem
Middleware registered in the wrong order causes subtle bugs — auth middleware running after business logic, logging missing context set by later middleware, or error handlers catching errors they should not. Order bugs are hard to detect.
Solution
Structure middleware in explicit layers with documented ordering: 1) infrastructure (logging, tracing), 2) security (CORS, CSRF, rate limiting), 3) auth (authentication, session), 4) authorization, 5) business logic. Use typed context objects that accumulate middleware-provided values.
Why
Explicit ordering makes middleware pipelines auditable. Typed context ensures downstream middleware cannot access values that upstream middleware has not yet populated.
Gotchas
- Express-style middleware uses mutable req objects, which makes typing accumulated context difficult — prefer framework-level context typing (Hono, Fastify schemas).
- Error handling middleware must be registered last in Express.
- Async middleware errors are not caught by Express error handlers unless you call next(err) — always wrap async handlers.
Code Snippets
Typed middleware context accumulation in Hono
import { Hono } from 'hono'
type Env = {
Variables: {
requestId: string
user: { id: string; role: string }
}
}
const app = new Hono<Env>()
// Layer 1: infrastructure
app.use('*', async (c, next) => {
c.set('requestId', crypto.randomUUID())
await next()
})
// Layer 2: auth
app.use('/api/*', async (c, next) => {
const user = await verifyToken(c.req.header('Authorization'))
if (!user) return c.json({ error: 'UNAUTHORIZED' }, 401)
c.set('user', user)
await next()
})
// Route handler — types are guaranteed by middleware layers
app.get('/api/profile', (c) => {
const user = c.get('user') // typed: { id: string; role: string }
return c.json(user)
})Revisions (0)
No revisions yet.