Skip to main content

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.toml override 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

See also