Using Middleware¶
A middleware in Litestar is any callable that receives at least one kwarg called app and returns an
ASGIApp. An ASGIApp is nothing but an async function that receives the ASGI
primitives scope , receive and send , and either calls the next ASGIApp or returns a response / handles
the websocket connection.
For example, the following function can be used as a middleware because it receives the app kwarg and returns
an ASGIApp:
from litestar.types import ASGIApp, Scope, Receive, Send
def middleware_factory(app: ASGIApp) -> ASGIApp:
async def my_middleware(scope: Scope, receive: Receive, send: Send) -> None:
# do something here
...
await app(scope, receive, send)
return my_middleware
We can then pass this middleware to the Litestar instance, where it will be called on
every request:
from litestar.types import ASGIApp, Scope, Receive, Send
from litestar import Litestar
def middleware_factory(app: ASGIApp) -> ASGIApp:
async def my_middleware(scope: Scope, receive: Receive, send: Send) -> None:
# do something here
...
await app(scope, receive, send)
return my_middleware
app = Litestar(route_handlers=[...], middleware=[middleware_factory])
In the above example, Litestar will call the middleware_factory function and pass to it app. It’s important to
understand that this kwarg does not designate the Litestar application but rather the next ASGIApp in the stack. It
will then insert the returned my_middleware function into the stack of every route in the application -
because we declared it on the application level.
Layered architecture
Middlewares are part of Litestar’s layered architecture* which means you can set them on every layer of the application.
You can read more about this here: Layered architecture
Middleware Call Order¶
due to the way we’re traversing over the app layers, the middleware stack is constructed in ‘application > handler’ order, which is the order we want the middleware to be called in.
using this order however, since each middleware wraps the next callable, the first middleware in the stack would up being the innermost wrapper, i.e. the last one to receive the request and the first one to see the response.
to achieve the intended call order, we perform the wrapping in reverse (‘handler -> application’).
graph TD
request --> M1
M1 --> M2
M2 --> H
H --> M2R
M2R --> M1R
M1R --> response
subgraph M1 [middleware_1]
M2
subgraph M2 [middleware_2]
H[handler]
end
end
style M1 stroke:#333,stroke-width:2px
style M2 stroke:#555,stroke-width:1.5px
style H stroke:#777,stroke-width:1px
from typing import TYPE_CHECKING
from litestar import Controller, Litestar, Router, get
from litestar.datastructures import State
from litestar.middleware import MiddlewareProtocol
if TYPE_CHECKING:
from litestar.types import ASGIApp, Receive, Scope, Send
def create_test_middleware(middleware_id: int) -> type[MiddlewareProtocol]:
class TestMiddleware(MiddlewareProtocol):
def __init__(self, app: "ASGIApp") -> None:
self.app = app
async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None:
litestar_app = scope["app"]
litestar_app.state.setdefault("middleware_calls", [])
litestar_app.state["middleware_calls"].append(middleware_id)
await self.app(scope, receive, send)
return TestMiddleware
class MyController(Controller):
path = "/controller"
middleware = [create_test_middleware(4), create_test_middleware(5)]
@get(
"/handler",
middleware=[create_test_middleware(6), create_test_middleware(7)],
)
async def my_handler(self, state: State) -> list[int]:
return state["middleware_calls"]
router = Router(
path="/router",
route_handlers=[MyController],
middleware=[create_test_middleware(2), create_test_middleware(3)],
)
app = Litestar(
route_handlers=[router],
middleware=[create_test_middleware(0), create_test_middleware(1)],
)
Run it
> curl http://127.0.0.1:8000/router/controller/handler
[0,1,2,3,4,5,6,7]
Middlewares and Exceptions¶
When an exception is raised by a route handler or a dependency it will be transformed into a response by an exception handler. This response will follow the normal “flow” of the application and therefore, middlewares are still applied to it.
As with any good rule, there are exceptions to it. In this case they are two exceptions raised by Litestar’s ASGI router:
They are raised before the middleware stack is called and will only be handled by exception
handlers defined on the Litestar instance itself. If you wish to modify error responses generated
from these exception, you will have to use an application layer exception handler.