Importing
Defining a Middleware
Create a middleware by instantiating theMiddleware class with a configuration object:
Configuration Options
request (optional)
Define request schemas that will be validated before the middleware executes. Uses Standard Schema-compatible libraries (Zod, Valibot, ArkType, etc.).
Supported fields:
body- Request body validationparams- Path parameter validationquery- Query string validationheaders- HTTP header validationcookies- Cookie validation
response (optional)
Define response schemas for different HTTP status codes. The middleware can return a Response object matching these schemas.
handler (required)
The middleware function that executes for each request. Receives a context object and can:
- Return an object to extend the context for subsequent middlewares and the route handler
- Return a
Responseto short-circuit the request - Return
undefinedorvoidif no context extension is needed
Using Middlewares
Route-Specific Middlewares
Apply middlewares to individual routes using themiddlewares array:
as const on the middlewares array to preserve type inference.
Global Middlewares
Apply middlewares to all routes by passing them to the API constructor:Context Extension
Returning Data
Middlewares can return an object to extend the context. This data becomes available to subsequent middlewares and the final route handler:Type Safety
Middleware data is fully typed. TypeScript automatically infers the types from the data you return:Execution Order
Multiple Middlewares
Middlewares execute in order, accumulating context data:- Global middlewares (from API constructor) run first
- Route-specific middlewares run after global middlewares
- Within each group, middlewares run in array order
- Each middleware can access data from previous middlewares
loggingMiddlewareexecutes → addsrequestStartTimeauthMiddlewareexecutes → addsuseradminRoleMiddlewareexecutes → checksuser.role- Route handler executes → has access to all context data
Early Return
Middlewares can return aResponse to short-circuit the request. When this happens, subsequent middlewares and the route handler won’t execute:
Schema Validation
Middlewares can define request and response schemas that are validated independently.Validation Behavior
- Each middleware validates its request data against its own schema before executing
- Route validates its request data against its own schema after all middlewares complete
- All schemas must pass validation - there is no merging or replacement
- Different properties (body vs. query vs. headers) from different middlewares and routes are all validated
Common Patterns
Authentication Middleware
Parameterized Middlewares
Create reusable middleware factories:CORS Middleware
Database Transaction Middleware
Request Context Middleware
Logging Middleware
Handler Context
The middleware handler receives a context object (c) with:
Request Data
c.req.body- Validated request bodyc.req.params- Validated path parametersc.req.query- Validated query parametersc.req.headers- Validated headersc.req.cookies- Validated cookiesc.raw- Underlying Request object
Response Methods
c.json(status, data)- JSON response with validationc.text(status, text)- Plain text responsec.html(status, html)- HTML responsec.redirect(status, url)- HTTP redirect
Context Accessors
c.get(key)- Access data from previous middlewares
Best Practices
- Use
as conston middleware arrays to preserve type inference - Return objects for context extension, not Response objects, unless you want to short-circuit
- Keep middlewares focused - each middleware should do one thing well
- Order matters - place auth before role checks, logging before everything
- Validate early - use request schemas to catch invalid data before handler execution
- Use parameterized factories for reusable middleware patterns
- Access previous middleware data using
c.get()in dependent middlewares