Overview
Middleware in Contract Kit provides a powerful way to handle cross-cutting concerns like authentication, logging, CORS, rate limiting, and error handling. Middleware functions intercept requests and responses, allowing you to implement common patterns without repeating code in every route handler.What is Middleware?
Middleware is a function that runs during the request/response lifecycle. It can:- Inspect requests before they reach your handlers
- Transform requests or responses
- Short-circuit the request (e.g., return early for auth failures)
- Add headers or metadata
- Log requests and responses
- Handle errors consistently
Request Lifecycle
When a request arrives, it flows through middleware in the order they’re registered:- Call
next(ctx)to pass control to the next middleware/handler - Return a response early to skip remaining middleware and the handler
- Transform the response returned by
next(ctx)before returning it
Built-in Middleware
Contract Kit provides several built-in middleware functions you can use out of the box.Logging Middleware
Log requests and responses with automatic duration tracking:logger: Logger instance withinfo()anderror()methodsrequestIdHeader: Header name to include request ID in responseonRequestStart: Custom hook called when request startsonRequestEnd: Custom hook called when request completes
CORS Middleware
Handle Cross-Origin Resource Sharing (CORS) headers:origins: Allowed origins ("*"for all, or array of specific origins)credentials: Allow credentials (cookies, authorization headers)methods: Allowed HTTP methodsheaders: Allowed request headers
- Handles OPTIONS preflight requests
- Adds appropriate CORS headers to all responses
- Echoes the request origin when credentials are enabled (CORS spec requirement)
Rate Limiting Middleware
Protect your API from abuse with rate limiting:- A
rateLimitport (provided by@contract-kit/provider-redisor@contract-kit/provider-rate-limit-upstash) - Rate limit metadata on contracts (see below)
"user": Limit per authenticated user (requiresctx.user.id)"ip": Limit per client IP address"global": Global limit across all requests
Error Mapping
Map errors to consistent HTTP responses:Creating Custom Middleware
Create your own middleware to handle application-specific concerns.Middleware Type
Authentication Middleware
Request Timing Middleware
Request ID Middleware
Security Headers Middleware
Middleware Composition
Middleware runs in the order specified. Order matters for dependencies:- Security headers first (apply to all responses)
- CORS early (handle preflight requests before auth)
- Logging early (capture full request lifecycle)
- Request ID early (available to all middleware)
- Authentication before authorization checks
- Rate limiting after authentication (use user context)
- Timing last (measure actual handler time)
Accessing Context and Metadata
Using Context
Context flows through middleware and can be modified:Using Metadata
Contract metadata allows middleware to behave differently per route:Short-Circuiting Requests
Middleware can return early to skip the handler:Testing Middleware
Test middleware in isolation or integration:Unit Testing
Integration Testing
Common Use Cases
API Key Authentication
Request Validation
Response Compression
Best Practices
Keep middleware focused
Keep middleware focused
Each middleware should have a single responsibility. Don’t combine logging, auth, and rate limiting in one middleware.
Order middleware carefully
Order middleware carefully
Middleware order matters. Security headers should be first, CORS before auth, authentication before authorization.
Use metadata for route-specific behavior
Use metadata for route-specific behavior
Don’t hardcode route patterns in middleware. Use contract metadata to declare which routes need specific middleware behavior.
Handle errors gracefully
Handle errors gracefully
Middleware should catch and handle errors appropriately. Don’t let errors in logging or metrics crash the request.
Avoid mutating context
Avoid mutating context
Create new context objects instead of mutating the existing one. This makes the flow easier to understand and debug.
Document middleware requirements
Document middleware requirements
If middleware requires specific context properties or ports, document them clearly with TypeScript types.
Test middleware independently
Test middleware independently
Write unit tests for middleware in isolation before testing the full pipeline.
Consider performance
Consider performance
Middleware runs on every request. Keep it fast and avoid expensive operations in the hot path.