Pounce supports server-level middleware for transforming HTTP requests and responses across your entire application. Middleware runs inside pounce's HTTP request pipeline, before and after your ASGI app. Lifespan, worker lifecycle, and WebSocket scopes bypass this middleware stack.
Configuration
Pass middleware as a list toServerConfig. They execute in order (first = outermost):
from pounce import ServerConfig, CORSMiddleware, SecurityHeadersMiddleware
config = ServerConfig(
middleware=[
CORSMiddleware(
allow_origin="https://example.com",
allow_methods="GET, POST",
allow_headers="Authorization",
),
SecurityHeadersMiddleware(),
],
)
Built-in Middleware
CORSMiddleware
Handles Cross-Origin Resource Sharing headers and preflight requests:
from pounce import CORSMiddleware
cors = CORSMiddleware(
allow_origin="https://example.com",
allow_methods="GET, POST, PUT, DELETE",
allow_headers="Authorization, Content-Type",
max_age=3600,
)
SecurityHeadersMiddleware
Injects security headers on every response:
from pounce import SecurityHeadersMiddleware
security = SecurityHeadersMiddleware()
# Adds: X-Content-Type-Options, X-Frame-Options, etc.
Custom Middleware
Middleware is classified by callable parameter count:
- 1 parameter: pre-request hook,
scope -> scope | Response - 2 parameters: exception hook,
(scope, exc) -> Response | None - 3 parameters: post-response hook,
(scope, status, headers) -> (status, headers)
from pounce import Response
# Pre-request: inspect scope, optionally short-circuit
async def auth_middleware(scope):
token = dict(scope["headers"]).get(b"authorization")
if not token:
return Response(status=401, body=b"Unauthorized")
scope["user"] = "authenticated"
return scope # continue to app
# Post-response: modify status or headers
async def timing_middleware(scope, status, headers):
headers.append((b"x-custom-header", b"value"))
return status, headers
# Exception: convert selected app errors into responses
async def exception_middleware(scope, exc):
if isinstance(exc, ValueError):
return Response(status=400, body=b"Bad Request")
return None # re-raise unhandled exceptions
Common Patterns
Request ID injection -- pounce injectsX-Request-IDautomatically (see Observability).
Rate limiting -- use the built-inrate_limit_enabledconfig option instead of middleware (see Backpressure).
Logging -- uselifecycle_logging=Truefor structured request events (see Lifecycle Logging).