How to Register Custom Middleware
This guide shows how to add custom ASGI middleware to the Framework M web stack without modifying core framework code.
What you need
- A Python package with a
pyproject.toml. - An ASGI middleware class.
- (Optional) A
framework_config.tomloverride if you want to disable or re-prioritize existing middleware.
Step 1: Write the middleware
Create a class that follows the ASGI callable contract:
# my_app/middleware.py
from litestar.types import ASGIApp, Message, Receive, Scope, Send
class RequestTimingMiddleware:
"""Example middleware that logs request duration."""
priority = 200 # Runs late, close to the handler
def __init__(self, app: ASGIApp) -> None:
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope.get("type") != "http":
await self.app(scope, receive, send)
return
import time
start = time.perf_counter()
async def wrapped_send(message: Message) -> None:
await send(message)
await self.app(scope, receive, wrapped_send)
duration = time.perf_counter() - start
path = scope.get("path", "")
print(f"{scope.get('method')} {path} took {duration:.4f}s")
The priority class attribute controls ordering. Lower values run first.
Step 2: Register the entry point
Add the middleware to your pyproject.toml under the framework_m.middleware group:
[project.entry-points."framework_m.middleware"]
timing = "my_app.middleware:RequestTimingMiddleware"
Step 3: Install the package
For local development, add your package to the workspace and install it in editable mode:
# From the project root
uv pip install -e ./libs/my-app
Step 4: Verify it is applied
Start the application and inspect the middleware stack, or run a request and check the expected side effect.
m serve
Overriding an existing middleware
To replace a framework middleware (for example, to provide your own recent-document tracking), register an entry point with the same name:
[project.entry-points."framework_m.middleware"]
recent_document = "my_app.middleware:MyRecentDocumentMiddleware"
Your class replaces the default RecentDocumentMiddleware because later entry-point registrations override earlier ones.
Configuring middleware at runtime
You can enable, disable, re-prioritize, or pass extra kwargs from framework_config.toml without changing code:
[web.middleware.timing]
enabled = true
priority = 150
kwargs = { log_level = "info" }
Access the kwarg in your middleware constructor:
class RequestTimingMiddleware:
def __init__(self, app: ASGIApp, log_level: str = "debug") -> None:
self.app = app
self.log_level = log_level
Disabling a default middleware
[web.middleware.recent_document]
enabled = false