Collection of middleware for Starlette applications
Project description
starlette-middleware-collection
A small collection of Starlette compatible middleware, with FastAPI compatibility and examples.
Included Middleware
SizeLimit: Rejects requests with bodies larger than a configured byte limit.RequestUUID: Adds a per-request UUID torequest.state.id.ResponseUUID: Sets anX-Api-Request-Idresponse header, propagating theRequestUUIDid when present.ResponseTime: Adds anX-Process-Timeresponse header with the request duration in seconds.RequestLogging: Logs one record per request, with a configurable structure and pluggable backend.
Installation
Install from PyPI:
pip install starlette-middleware-collection
Or with uv:
uv add starlette-middleware-collection
SizeLimit
The SizeLimit middleware checks the incoming content-length header and returns 413 Content Too Large when the request body exceeds the configured limit.
- Constructor argument:
limit(bytes) - Environment variable:
MW_REQUEST_BODY_LIMIT - Default:
10 * 1024 * 1024(10 MB)
Basic Usage (SizeLimit)
from fastapi import FastAPI
from starlette_middleware_collection import SizeLimit
app = FastAPI()
app.add_middleware(SizeLimit)
With a Custom Limit
from fastapi import FastAPI
from starlette_middleware_collection import SizeLimit
app = FastAPI()
app.add_middleware(SizeLimit, limit=5 * 1024 * 1024) # 5 MB
Using Environment Variable (SizeLimit)
import os
from fastapi import FastAPI
from starlette_middleware_collection import SizeLimit
os.environ["MW_REQUEST_BODY_LIMIT"] = str(1024 * 1024) # 1 MB
app = FastAPI()
app.add_middleware(SizeLimit)
[!NOTE] The constructor argument takes precedence over the environment variable.
RequestUUID
The RequestUUID middleware sets request.state.id for every request.
- Constructor argument:
uuid_version - Environment variable:
MW_UUID_VERSION - Supported versions:
4,7 - Default:
7
Basic Usage (Default UUIDv7)
from fastapi import FastAPI, Request
from starlette_middleware_collection import RequestUUID
app = FastAPI()
app.add_middleware(RequestUUID)
@app.post("/")
async def read_root(request: Request):
return {"uuid": str(request.state.id)}
With a Custom UUID Version
from fastapi import FastAPI
from starlette_middleware_collection import RequestUUID
app = FastAPI()
app.add_middleware(RequestUUID, uuid_version=4)
Using Environment Variable (RequestUUID)
import os
from fastapi import FastAPI
from starlette_middleware_collection import RequestUUID
os.environ["MW_UUID_VERSION"] = "4"
app = FastAPI()
app.add_middleware(RequestUUID)
[!NOTE] The constructor argument takes precedence over the environment variable.
If an unsupported uuid_version is provided, a ValueError is raised.
ResponseUUID
The ResponseUUID middleware sets a request-id header (X-Api-Request-Id by default) on
every response. When RequestUUID is also installed it propagates the same
request.state.id; otherwise it generates a fresh UUID so the header is always present.
- Constructor arguments:
header_name,uuid_version - Environment variable:
MW_RESPONSE_ID_HEADER(header name) - Default header:
X-Api-Request-Id - Supported versions:
4,7(default7, used only when generating a fresh id)
Basic Usage (ResponseUUID)
from fastapi import FastAPI
from starlette_middleware_collection import ResponseUUID
app = FastAPI()
app.add_middleware(ResponseUUID) # generates a fresh id per response
Paired with RequestUUID
from fastapi import FastAPI
from starlette_middleware_collection import RequestUUID, ResponseUUID
app = FastAPI()
app.add_middleware(ResponseUUID) # reads request.state.id ...
app.add_middleware(RequestUUID) # ... which this sets
With a Custom Header (ResponseUUID)
from fastapi import FastAPI
from starlette_middleware_collection import ResponseUUID
app = FastAPI()
app.add_middleware(ResponseUUID, header_name="X-Trace-Id")
[!NOTE] The constructor argument takes precedence over the environment variable.
ResponseTime
The ResponseTime middleware measures how long each request takes and sets the elapsed
time (in seconds) on a response header (X-Process-Time by default).
- Constructor argument:
header_name - Environment variable:
MW_TIMING_HEADER(header name) - Default header:
X-Process-Time
Basic Usage (ResponseTime)
from fastapi import FastAPI
from starlette_middleware_collection import ResponseTime
app = FastAPI()
app.add_middleware(ResponseTime) # e.g. X-Process-Time: 0.001234
With a Custom Header (ResponseTime)
from fastapi import FastAPI
from starlette_middleware_collection import ResponseTime
app = FastAPI()
app.add_middleware(ResponseTime, header_name="X-Elapsed-Seconds")
[!NOTE] The constructor argument takes precedence over the environment variable.
RequestLogging
The RequestLogging middleware emits one log record per request. By default it logs a
structured dict to a standard-library logger; you can customize the record with a
formatter and route it anywhere with a handler (a sync or async callable — write to a
file, database, queue, etc.). It pairs with RequestUUID: when a request id exists it is
included as request_id.
- Constructor arguments:
logger,level,handler,formatter - Environment variable:
MW_REQUEST_LOG_LEVEL(log level, e.g.20forINFO) - Default logger:
starlette_middleware_collection.request - Default level:
logging.INFO
The default record looks like:
{
"request_id": "019f84...", # or None when RequestUUID is not installed
"method": "POST",
"path": "/upload",
"status_code": 200,
"duration_ms": 12.3,
"client": "127.0.0.1",
}
Basic Usage (RequestLogging)
import logging
from fastapi import FastAPI
from starlette_middleware_collection import RequestLogging
logging.basicConfig(level=logging.INFO)
app = FastAPI()
app.add_middleware(RequestLogging)
Custom Backend (file, database, ...)
from fastapi import FastAPI
from starlette_middleware_collection import RequestLogging
async def to_database(record: dict) -> None:
await db.access_logs.insert(record) # your async backend
app = FastAPI()
app.add_middleware(RequestLogging, handler=to_database)
Custom Record Structure
from fastapi import FastAPI, Request
from starlette.responses import Response
from starlette_middleware_collection import RequestLogging
def formatter(request: Request, response: Response, elapsed: float) -> str:
return f"{request.method} {request.url.path} -> {response.status_code} ({elapsed:.3f}s)"
app = FastAPI()
app.add_middleware(RequestLogging, formatter=formatter)
[!NOTE] A failing
handlernever breaks the request; the error is logged instead.
Combined Example
from fastapi import FastAPI, Request
from starlette_middleware_collection import (
RequestLogging,
RequestUUID,
ResponseTime,
ResponseUUID,
SizeLimit,
)
app = FastAPI()
# Middleware run outermost-first (last added runs first on the way in).
app.add_middleware(RequestLogging)
app.add_middleware(ResponseTime) # X-Process-Time
app.add_middleware(ResponseUUID) # X-Api-Request-Id (propagated below)
app.add_middleware(SizeLimit, limit=2 * 1024 * 1024) # 2 MB
app.add_middleware(RequestUUID, uuid_version=7) # sets request.state.id
@app.post("/upload")
async def upload(request: Request):
return {"request_id": str(request.state.id)}
Changelog
See CHANGELOG.md for a detailed list of changes and version history.
Requirements
Currently supports only Python 3.14 with latest version of Starlette. We plan on supporting older versions of Python and Starlette in the near future.
Contributing
For bugs / feature requests please submit issues. If you would like to contribute to this project, you are welcome to submit a pull request
Warranty / Liability / Official support
This project is being developed independently, we provide the package "as-is" without any implied warranty or liability, usage is your own responsibility
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file starlette_middleware_collection-0.2.0.tar.gz.
File metadata
- Download URL: starlette_middleware_collection-0.2.0.tar.gz
- Upload date:
- Size: 6.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4171a4fa95a725c32c4de6f214564b2b7fb1acfb685bc6194051b719cc2ce174
|
|
| MD5 |
91f8f332252838276f528883cb515cf6
|
|
| BLAKE2b-256 |
d671c5cf311f8e3b98ce42f72cb5f33e7c06c3089f9e9b5e213988a5ea7cede0
|
File details
Details for the file starlette_middleware_collection-0.2.0-py3-none-any.whl.
File metadata
- Download URL: starlette_middleware_collection-0.2.0-py3-none-any.whl
- Upload date:
- Size: 9.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff6e404c398a69e971a12e4fce0a9c7da042e7b0f3543e864e1947eca13f3f3d
|
|
| MD5 |
71017907474eded94c5cf20ebf2f0914
|
|
| BLAKE2b-256 |
46b93a9591fa3fe385c0684ca362d6f2e5cec0225f880be6623e2e4fa3abd659
|