Middleware API
Reference for built-in middleware and custom middleware patterns in itty-spec.
Built-in Middleware
withMatchingContractOperation
Finds and sets the matching contract operation from a contract.
Type: RequestHandler<IRequest>
Usage: Automatically included in router's before array.
Purpose: Matches incoming requests to contract operations based on method and path pattern.
import { withMatchingContractOperation } from "itty-spec/middleware";
// Automatically included, but can be used manually:
const middleware = withMatchingContractOperation(contract, basePath);withSpecValidation
Validates path params, query params, headers, and body against contract schemas.
Type: RequestHandler<IRequest>
Usage: Automatically included in router's before array.
Purpose: Validates all request data before handlers run.
import { withSpecValidation } from "itty-spec/middleware";
// Automatically includedwithResponseHelpers
Attaches typed respond() method to request object.
Type: RequestHandler<IRequest>
Usage: Automatically included in router's before array.
Purpose: Provides type-safe response helper method.
import { withResponseHelpers } from "itty-spec/middleware";
// Automatically included
// Makes request.respond() available in handlerswithContractFormat
Formats contract response objects to Response objects.
Type: ResponseHandler
Usage: Automatically included in router's finally array.
Purpose: Converts contract response format to HTTP Response.
import { withContractFormat } from "itty-spec/middleware";
// Automatically included
// Can be customized:
const router = createRouter({
contract,
handlers,
format: withContractFormat(customFormatter),
});withContractErrorHandler
Handles validation errors and other errors.
Type: (err: unknown, request: RequestType, ...args: Args) => Response
Usage: Automatically set as router's catch handler.
Purpose: Converts errors to appropriate HTTP responses.
import { withContractErrorHandler } from "itty-spec/middleware";
// Automatically included
// Can be customized:
const router = Router({
catch: withContractErrorHandler(),
});withMissingHandler
Handles missing routes (404s).
Type: ResponseHandler
Usage: Automatically included in router's finally array.
Purpose: Returns 404 for unmatched routes.
import { withMissingHandler } from "itty-spec/middleware";
// Automatically included
// Can be customized:
const router = createRouter({
contract,
handlers,
missing: async (request) => {
return request.respond({
status: 404,
contentType: "application/json",
body: { error: "Not Found" },
});
},
});Middleware Types
RequestHandler
Type for before middleware.
type RequestHandler<
RequestType extends IRequest = IRequest,
Args extends any[] = any[]
> = (
request: RequestType,
...args: Args
) => void | Promise<void> | Response | Promise<Response>If a middleware returns a Response, it short-circuits the request.
ResponseHandler
Type for finally middleware.
type ResponseHandler = (
response: unknown,
request: IRequest
) => Response | Promise<Response>Custom Middleware Patterns
Authentication Middleware
async function withAuth(request: IRequest) {
const authHeader = request.headers.get("authorization");
if (!authHeader) {
throw new Error("Unauthorized");
}
const user = await verifyToken(authHeader);
(request as any).user = user;
}
const router = createRouter({
contract,
handlers,
before: [withAuth],
});Logging Middleware
async function withLogging(request: IRequest) {
const startTime = Date.now();
(request as any).startTime = startTime;
console.log(`${request.method} ${request.url}`);
}
const router = createRouter({
contract,
handlers,
before: [withLogging],
finally: [
async (response, request) => {
const startTime = (request as any).startTime;
if (startTime) {
const duration = Date.now() - startTime;
console.log(`Duration: ${duration}ms`);
}
return response;
},
],
});CORS Middleware
function withCORS(allowedOrigins: string[] = ["*"]) {
return async (response: Response, request: IRequest) => {
const origin = request.headers.get("origin");
if (allowedOrigins.includes("*") || (origin && allowedOrigins.includes(origin))) {
response.headers.set("access-control-allow-origin", origin || "*");
response.headers.set("access-control-allow-methods", "GET, POST, PUT, DELETE");
response.headers.set("access-control-allow-headers", "Content-Type, Authorization");
}
return response;
};
}
const router = createRouter({
contract,
handlers,
finally: [withCORS(["https://example.com"])],
});Rate Limiting Middleware
const rateLimiter = new Map<string, { count: number; resetAt: number }>();
function withRateLimit(maxRequests: number = 100, windowMs: number = 60000) {
return async (request: IRequest) => {
const key = request.headers.get("x-forwarded-for") || "unknown";
const now = Date.now();
const limit = rateLimiter.get(key);
if (limit && limit.resetAt > now) {
if (limit.count >= maxRequests) {
throw new Error("Rate limit exceeded");
}
limit.count++;
} else {
rateLimiter.set(key, { count: 1, resetAt: now + windowMs });
}
};
}
const router = createRouter({
contract,
handlers,
before: [withRateLimit(100, 60000)],
});Middleware Ordering
Middleware runs in the order specified:
const router = createRouter({
contract,
handlers,
before: [
middleware1, // Runs first
middleware2, // Runs second
middleware3, // Runs third
],
finally: [
middleware4, // Runs first (after handler)
middleware5, // Runs second
],
});Built-in Middleware Order
Built-in middleware always runs in a specific order:
Before:
withParamswithMatchingContractOperationwithSpecValidationwithResponseHelpers- Your custom
beforemiddleware
Finally:
withMissingHandlerwithContractFormat- Your custom
finallymiddleware
Short-Circuiting
If a before middleware returns a Response, it short-circuits the request:
async function withAuth(request: IRequest) {
const auth = request.headers.get("authorization");
if (!auth) {
// Short-circuit: return error immediately
return new Response(
JSON.stringify({ error: "Unauthorized" }),
{ status: 401 }
);
}
// Continue to next middleware/handler
}Related
- Middleware Guide - Learn about middleware patterns
- Router Configuration - Configure middleware
- Error Handling - Handle errors in middleware