swadhinbiswas πŸš€ release: 2.0.8 β€” write-path fix (ADR-080), fork-safe GIL pool (ADR-081), HTTP/3 (ADR-082), Security DI fix, docs honesty pass Β· 8/6/2026
731fffe
185 lines Β· 5.2 KB
Raw Download

title: Middleware
description: Intercept requests and responses with JustAPI middleware for logging, timing, CORS, and custom processing.
keywords: middleware, CORS, request interception, JustAPI, Rust middleware

Middleware lets you run code before and after every request. JustAPI has two
middleware layers:

  • Native Rust middleware (CORS, JWT, security headers) β€” configured with
    dedicated methods, runs on every request including the native fast path.
  • Python middleware β€” your own async def mw(request, call_next) callables,
    registered with @app.middleware("http") or per-route middlewares=.

Basic HTTP Middleware

Use the @app.middleware("http") decorator. Middleware is async and must
await call_next(request):

import time
from justapi import JustAPIApp

app = JustAPIApp()


@app.middleware("http")
async def timing_middleware(request, call_next):
    start = time.time()
    response = await call_next(request)
    elapsed = time.time() - start
    response.headers.append(
        (b"X-Process-Time", str(round(elapsed, 4)).encode())
    )
    return response

call_next dispatches the request down the chain; the return value is the
handler's response object.

Performance: Python middleware runs on the Python side of the boundary and
is bypassed (must be) by the native fast path. Prefer native middleware
(add_cors, enable_secure_headers, set_jwt_auth) when possible. Keep
Python middleware to logic that must run in Python.

CORS Middleware

JustAPI has a built-in, Rust-native CORS configuration method:

app.add_cors(
    allow_origins=["https://example.com", "https://api.example.com"],
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["*"],
    allow_credentials=True,
)
Parameter Type Default Description
allow_origins list[str] ["*"] Allowed origins
allow_methods list[str] All methods Allowed HTTP methods
allow_headers list[str] ["*"] Allowed headers
allow_credentials bool False Allow cookies/auth headers

Secure Headers Middleware

Add safe HTTP security headers (Rust-native):

app.enable_secure_headers(with_hsts=True)

This adds:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Strict-Transport-Security (when with_hsts=True)
  • Content-Security-Policy
  • X-XSS-Protection: 0

Multiple Middleware

Middlewares are executed in registration order (first added is outermost):

@app.middleware("http")
async def middleware_one(request, call_next):
    print("Before 1")
    response = await call_next(request)
    print("After 1")
    return response


@app.middleware("http")
async def middleware_two(request, call_next):
    print("Before 2")
    response = await call_next(request)
    print("After 2")
    return response

Output for a single request:

Before 1
Before 2
[handler executes]
After 2
After 1

Route-Level Middleware

Attach middleware to individual routes:

async def auth_middleware(request, call_next):
    if not (request.get("headers") or {}).get("x-token"):
        return {"status": 401, "body": {"detail": "unauthorized"}}
    return await call_next(request)

@app.get("/protected", middlewares=[auth_middleware])
async def protected(request):
    return {"message": "top secret"}

Real-World Patterns

Request Logging Middleware

import logging

logger = logging.getLogger("justapi")


@app.middleware("http")
async def log_requests(request, call_next):
    method = request.get("method")
    path = request.get("path")
    logger.info("β†’ %s %s", method, path)
    response = await call_next(request)
    status = getattr(response, "status_code", None) or 200
    logger.info("← %s %s β†’ %s", method, path, status)
    return response

Authorization via Dependency

from justapi import Depends, HTTPException, Header


def require_admin(authorization: str = Header(...)):
    if authorization != "Bearer admin-token":
        raise HTTPException(403, "Admin access required")


@app.get("/admin/users", dependencies=[Depends(require_admin)])
def list_all_users(request):
    return {"users": []}

Native Fast-Path Note

The Rust native fast path (native=True) is a validate-and-echo shortcut that
does not execute Python middleware or dependencies. Routes
using native=True must not declare Python middleware/dependencies β€” the
framework rejects that combination at registration time. Native middleware
(add_cors, enable_secure_headers, set_jwt_auth) runs for all routes.

How It Works Internally

  1. Native middleware (CORS, security headers, JWT, rate limit) runs in the
    Rust middleware chain with zero GIL overhead.
  2. Python middleware runs as an async wrapper around your handler on the
    Python side.
  3. call_next dispatches down the middleware chain to the handler.
  4. The handler's result flows back through each middleware's post-processing.

Next Steps