kludex/starlette

The little ASGI framework that shines. 🌟

12,218 stars Python 10 components

12 hidden assumptions · 7-stage pipeline · 10 components

Like any codebase, this repository makes assumptions it never checks — most are routine. The ones worth your attention are below.

Processes ASGI requests through middleware chain to route handlers and returns responses

ASGI server sends request scope to Starlette application, which passes it through middleware pipeline in order. Each middleware can modify the request or short-circuit to return a response. The Router matches the request path to a configured route and calls the corresponding handler function. The handler processes the request data and returns a Response object, which flows back through the middleware stack in reverse order before being serialized to ASGI messages.

Under the hood, the system uses 2 feedback loops, 4 data pools, 3 control points to manage its runtime behavior.

A 10-component repository. 68 files analyzed. Data flows through 7 distinct pipeline stages.

Hidden Assumptions

Most of what this code assumes is routine. These 3 are the ones most likely to cause trouble here. The rest are minor; they're under "Show everything".

Worth your attention first

Large file uploads or streaming data will cause memory exhaustion and crash the server. A 1GB file upload will consume 1GB+ RAM per concurrent request.

Worth your attention first

If a handler returns None, dict, or string instead of Response, the framework will crash with AttributeError when trying to serialize the return value to ASGI messages.

Worth your attention first

If any background task raises an exception, all subsequent tasks in the list are skipped silently. Critical cleanup tasks (database connections, file handles) may never execute.

Show everything (9 more)
Resource

Multipart form fields fit in memory as bytearray objects. Each MultipartPart stores field data in a bytearray that grows as data arrives.

If this fails: Large multipart fields (like base64-encoded files submitted as form data) will consume unbounded memory. A 100MB base64 field will use ~133MB RAM per concurrent upload.

starlette/formparsers.py:MultipartPart.data
Environment

Environment variables are only set during application startup before any values are read. The _has_been_read tracking prevents modification of already-accessed env vars.

If this fails: Runtime configuration updates fail silently or crash with EnvironError. Hot configuration reloads in production become impossible once the app has started reading config values.

starlette/config.py:Environ.__setitem__
Ordering

Middleware order in the sequence parameter determines execution order. The framework doesn't validate or document that ServerErrorMiddleware must be outermost and ExceptionMiddleware innermost.

If this fails: If users place their own middleware outside ServerErrorMiddleware, unhandled exceptions won't be caught properly. Request/response modifications by incorrectly ordered middleware can bypass security checks.

starlette/applications.py:Starlette.__init__
Contract

allow_origin_regex parameter contains valid regex syntax. The code compiles it with re.compile(allow_origin_regex) without validation.

If this fails: Invalid regex patterns cause the application to crash at startup with re.error. The error message doesn't clearly indicate it's from CORS configuration, making debugging difficult.

starlette/middleware/cors.py:CORSMiddleware.__init__
Scale

max_age=600 seconds is appropriate for all use cases. The default 10-minute preflight cache duration is hardcoded without considering varying security requirements.

If this fails: High-security applications may cache preflight responses too long, while high-traffic APIs may generate excessive preflight requests due to short cache duration. No guidance provided for tuning this value.

starlette/middleware/cors.py:max_age
Domain

HTTP method names are always uppercase strings matching exact method names ('GET', 'POST', etc). The code checks for lowercase method names as attributes but stores uppercase strings.

If this fails: Custom HTTP methods or case-sensitive method handling won't work correctly. Non-standard methods like 'PATCH' variants or WebDAV methods ('PROPFIND') are silently ignored.

starlette/endpoints.py:HTTPEndpoint._allowed_methods
Resource

is_async_callable() correctly detects async functions and that threadpool execution is always available. No fallback handling if threadpool is unavailable.

If this fails: If threadpool is exhausted or unavailable, sync background tasks will block the event loop indefinitely. Incorrect async detection could cause sync functions to be awaited directly.

starlette/background.py:BackgroundTask.is_async
Contract

Exception handlers are callable objects that accept (request, exc) parameters and return Response objects. No signature validation is performed when adding handlers.

If this fails: Invalid exception handlers cause crashes when exceptions occur rather than at registration time. A handler that expects different parameters will fail with TypeError during request processing.

starlette/middleware/exceptions.py:ExceptionMiddleware.add_exception_handler
Temporal

Environment variable access patterns are single-threaded or that the _has_been_read set is thread-safe. The tracking uses a plain Python set without locking.

If this fails: In multi-threaded scenarios (like with threading-based ASGI servers), race conditions can occur where env vars appear unread when they were actually read by another thread, leading to inconsistent EnvironError exceptions.

starlette/config.py:Environ._has_been_read

Open the standalone hidden-assumptions report for starlette →

How Data Flows Through the System

ASGI server sends request scope to Starlette application, which passes it through middleware pipeline in order. Each middleware can modify the request or short-circuit to return a response. The Router matches the request path to a configured route and calls the corresponding handler function. The handler processes the request data and returns a Response object, which flows back through the middleware stack in reverse order before being serialized to ASGI messages.

  1. ASGI Connection — ASGI server (like uvicorn) receives HTTP/WebSocket connection and creates scope dict with request metadata (method, path, headers, client info)
  2. Application Entry — Starlette.__call__ method receives scope and creates middleware stack with ServerErrorMiddleware wrapping ExceptionMiddleware wrapping user middleware wrapping Router [ASGIScope]
  3. Middleware Processing — Each middleware in the stack can inspect/modify the request, call downstream middleware, then inspect/modify the response on the way back up [ASGIScope → Request]
  4. Request Creation — Router or middleware creates Request object from ASGI scope, providing lazy access to body content, form data, JSON parsing, and headers [ASGIScope → Request]
  5. Route Matching — Router.route() compiles path patterns to regex, matches against request.path, extracts path parameters using Convertors, and selects handler function [Request]
  6. Handler Processing — Route endpoint function receives Request object, processes business logic (potentially calling request.json(), request.form(), etc.), returns Response [Request → Response]
  7. Response Serialization — Response object is converted to ASGI messages (http.response.start with status/headers, then http.response.body chunks), background tasks executed after send [Response]

Data Models

The data structures that flow between stages — the contracts that hold the system together.

ASGIScope starlette/types.py
dict with type: str ('http'|'websocket'), method: str, path: str, headers: list[tuple[bytes, bytes]], query_string: bytes, client: tuple[str, int]
Created by ASGI server, passed through middleware chain, consumed by Request/WebSocket constructors
Request starlette/requests.py
object with method: str, url: URL, headers: Headers, path_params: dict, query_params: QueryParams, plus async methods for body(), json(), form()
Built from ASGI scope in middleware, passed to route handlers, provides lazy access to request data
Response starlette/responses.py
object with status_code: int, headers: MutableHeaders, media_type: str, background: BackgroundTask, plus body content
Created by handlers with content and metadata, serialized to ASGI messages by framework
FormData starlette/datastructures.py
MultiDict-like with string keys mapping to str|UploadFile values, supports getlist() for multiple values per key
Parsed from multipart/form-encoded request bodies by FormParser, accessed by handlers via request.form()
Route starlette/routing.py
object with path: str, endpoint: callable, methods: list[str], name: str, includes_in_schema: bool
Defined in application config, compiled into regex patterns, matched against incoming request paths
BackgroundTask starlette/background.py
object with func: callable, args: tuple, kwargs: dict, is_async: bool
Created by handlers for post-response work, executed by framework after response is sent

System Behavior

How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.

Data Pools

Route Registry (registry)
Router maintains list of compiled route patterns with associated handlers, path parameter extractors, and HTTP method filters
Exception Handlers (registry)
Mapping of exception types to handler functions, stored in ASGI scope for access by middleware
Request Body Cache (cache)
_CachedRequest stores entire request body in memory when accessed to allow multiple reads by middleware
Application State (state-store)
State object attached to application for sharing data across requests, accessible via request.state

Feedback Loops

Delays

Control Points

Technology Stack

anyio (runtime)
Provides async/await compatibility layer and thread pool execution for sync functions
python-multipart (library)
Parses multipart/form-data request bodies including file uploads in FormParser
typing_extensions (library)
Provides type hints and protocols for older Python versions
itsdangerous (library)
Optional dependency for cryptographically signed session cookies and tokens
jinja2 (library)
Optional template engine for generating HTML responses
httpx (testing)
Async HTTP client used by TestClient for integration testing

Key Components

Explore the interactive analysis

See the full architecture map, data flow, and code patterns visualization.

Analyze on CodeSea

Related Repository Repositories

Frequently Asked Questions

What is starlette used for?

Processes ASGI requests through middleware chain to route handlers and returns responses kludex/starlette is a 10-component repository written in Python. Data flows through 7 distinct pipeline stages. The codebase contains 68 files.

How is starlette architected?

starlette is organized into 5 architecture layers: Application Core, Middleware Pipeline, Request Routing, Data Handling, and 1 more. Data flows through 7 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.

How does data flow through starlette?

Data moves through 7 stages: ASGI Connection → Application Entry → Middleware Processing → Request Creation → Route Matching → .... ASGI server sends request scope to Starlette application, which passes it through middleware pipeline in order. Each middleware can modify the request or short-circuit to return a response. The Router matches the request path to a configured route and calls the corresponding handler function. The handler processes the request data and returns a Response object, which flows back through the middleware stack in reverse order before being serialized to ASGI messages. This pipeline design reflects a complex multi-stage processing system.

What technologies does starlette use?

The core stack includes anyio (Provides async/await compatibility layer and thread pool execution for sync functions), python-multipart (Parses multipart/form-data request bodies including file uploads in FormParser), typing_extensions (Provides type hints and protocols for older Python versions), itsdangerous (Optional dependency for cryptographically signed session cookies and tokens), jinja2 (Optional template engine for generating HTML responses), httpx (Async HTTP client used by TestClient for integration testing). A focused set of dependencies that keeps the build manageable.

What system dynamics does starlette have?

starlette exhibits 4 data pools (Route Registry, Exception Handlers), 2 feedback loops, 3 control points, 3 delays. The feedback loops handle recursive and circuit-breaker. These runtime behaviors shape how the system responds to load, failures, and configuration changes.

What design patterns does starlette use?

6 design patterns detected: ASGI Protocol, Middleware Pipeline, Lazy Loading, Async/Sync Compatibility, Dependency Injection, and 1 more.

Analyzed on April 20, 2026 by CodeSea. Written by .