Middleware

Ingenium middleware uses the same async dispatch model as Koa - await next() in the middle, do work before and after. The signature is a typed ctx plus a zero-argument next:

type IngeniumMiddleware = (ctx: IngeniumContext, next: () => Promise<void>) => unknown | Promise<unknown>

Errors thrown anywhere in the chain bubble up to app.onError. See Errors for the boundary.

A typical middleware

app.use(async (ctx, next) => {
  const start = Date.now()
  ctx.state.requestId = crypto.randomUUID()
  try {
    await next()
  } finally {
    console.log(`${ctx.method} ${ctx.path} -> ${ctx._statusCode} ${Date.now() - start}ms`)
  }
})

Mounting middleware on a path prefix

app.use('/admin', adminOnlyMiddleware)
app.use('/admin', adminRouter)               // mount middleware OR router

Path-mounted middleware only runs for requests under the prefix. Internally the prefix is stripped before the inner chain runs, so mounted routers see paths relative to their mount point.

Built-in middleware

Ingenium ships with built-ins for the common production needs - all opt-in:

Where to next?

  • Errors - error classes and the onError boundary.
  • Body parsing - lazy ctx.body.* parsers.
  • CORS - built-in CORS middleware.
  • CSRF - built-in CSRF protection.