Middlewares
Middlewares wrap around route handlers to implement cross‑cutting behaviors such as authentication, logging, CORS, and request/response shaping. There are two types:
- Global Middlewares: Applied to every request handled by a router.
- Endpoint Middlewares: Defined per endpoint; executed with typed request context.
Overview
Key Concepts
- Middlewares enable the verification, enrichment, or modification of the request/response surrounding the handler.
- Endpoint middlewares run with full type inference for
params,searchParams, andbody. - Global middlewares apply to all requests handled by a router and operate on the original
Request.
Type Inference
Endpoint middlewares are type‑safe based on the endpoint’s Zod schemas for params, searchParams, and body. These types are inferred automatically from the endpoint configuration.
Global middlewares do not receive the typed context; they operate on the original Request.
Middlewares
Endpoint Middlewares
Endpoint middlewares are attached declaratively using createEndpointConfig. This approach ensures schema-based type safety.
Basic Usage
The following example demonstrates basic middleware usage without relying on the request context.
import { createEndpointConfig, createEndpoint } from "@aura-stack/router"
const config = createEndpointConfig({
use: [
async (ctx) => {
console.log(`Request to ${ctx.url}`)
return ctx
},
],
})
const handler = async () => Response.json({})
const endpoint = createEndpoint("GET", "/protected", handler, config)Context Usage
Context usage offers type inference powered by Zod Schemas, enabling type-safe validation and value mapping. The example below shows an endpoint middleware utilizing the request context.
import { z } from "zod"
import { createEndpoint, createEndpointConfig } from "@aura-stack/router"
const verifyToken = (token: string) => {
/* Add logic */
return token
}
const authConfig = createEndpointConfig({
use: [
async (ctx) => {
const token = ctx.request.headers.get("authorization")
if (!token || !token.startsWith("Bearer ")) {
throw new Error("Unauthorized")
}
/*
Verify token (simplified)
*/
const userId = verifyToken(token.slice(7))
/*
Add user info to context
*/
ctx.headers.setHeader("x-user-id", userId)
return ctx
},
],
})
const getProfile = createEndpoint(
"GET",
"/profile",
async (ctx) => {
const userId = ctx.headers.getHeader("x-user-id")
return Response.json({ userId, name: "John" })
},
authConfig
)Global Middlewares
Global middlewares are applied to all endpoints within a router via the createRouter configuration.
Middleware Chaining
Multiple middlewares execute in the order they are defined.
const config = createEndpointConfig({
use: [
/*
1. First middleware - logging
*/
async (ctx) => {
console.log("1. Logging request")
return ctx
},
/*
2. Second middleware - auth
*/
async (ctx) => {
console.log("2. Checking auth")
const token = ctx.request.headers.get("authorization")
if (!token) throw new Error("Unauthorized")
return ctx
},
/*
3. Third middleware - add metadata
*/
async (ctx) => {
console.log("3. Adding metadata")
ctx.headers.setHeader("x-processed", "true")
return ctx
},
],
})Execution Order
When both global and endpoint middlewares are defined, they execute in the following sequence:
- Global middlewares (from
createRouter) - Endpoint middlewares (from
createEndpointConfig) - Route handler
Example
import { createEndpoint, createEndpointConfig, createRouter } from "@aura-stack/router"
const endpointConfig = createEndpointConfig({
use: [
async (ctx) => {
console.log("2. Endpoint middleware")
return ctx
},
],
})
const endpoint = createEndpoint(
"GET",
"/test",
async (ctx) => {
console.log("3. Handler")
return Response.json({ ok: true })
},
endpointConfig
)
const router = createRouter([endpoint], {
use: [
async (ctx) => {
console.log("1. Global middleware")
return ctx
},
],
})