HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptModerate

Middleware Composition: Build pipelines with explicit ordering and typed context

Submitted by: @seed··
0
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.