Quickstart

Installation

This library’s only dependency is Starlette. It works with all Starlette-based frameworks, including FastAPI.

pip install starlette-context

Basic Usage

The context object is accessible when:

  1. You’re within a request-response cycle

  2. You’ve used either ContextMiddleware or RawContextMiddleware in your ASGI app

Minimal Working Example

# app.py
from starlette.middleware import Middleware
from starlette.applications import Starlette

from starlette_context.middleware import ContextMiddleware

middleware = [Middleware(ContextMiddleware)]
app = Starlette(middleware=middleware)
# views.py
from starlette.requests import Request
from starlette.responses import JSONResponse

from starlette_context import context

from .app import app

@app.route("/")
async def index(request: Request):
    # Access context data
    context["user_id"] = "12345"

    # Return context data in response
    return JSONResponse(context.data)

Adding Plugins

Plugins automatically populate the context with useful information from request headers:

from starlette.middleware import Middleware
from starlette.applications import Starlette

from starlette_context import plugins
from starlette_context.middleware import ContextMiddleware

middleware = [
    Middleware(
        ContextMiddleware,
        plugins=(
            plugins.RequestIdPlugin(),
            plugins.CorrelationIdPlugin()
        )
    )
]

app = Starlette(middleware=middleware)

With this setup, every request will have:

  • A request ID (generated if not provided in headers)

  • A correlation ID (generated if not provided in headers)

These values are accessible via context.data and are also included in the response headers.

Using with Logging

One of the main benefits of starlette-context is enriching your logs with request data:

import structlog
from starlette_context import context


def add_context(
    logger, method_name, event_dict
):
    """Structlog processor that merges context data into every log entry."""
    if context.exists():
        event_dict.update(context.data)
    return event_dict


structlog.configure(
    processors=[
        add_context,
        structlog.dev.ConsoleRenderer(),
    ]
)

logger = structlog.get_logger()


@app.route("/")
async def index(request: Request):
    logger.info("Processing request")
    return JSONResponse({"message": "Hello World"})

The context.exists() guard is important — without it, any log emitted outside a request cycle (e.g. during startup) would raise ContextDoesNotExistError.

For a complete working example with JSON logging, see the Examples page.