4.1 KiB
4.1 KiB
Starlette source notes
Repo: encode/starlette
Local checkout: /home/ubuntu/repos/rodin-sources/starlette
Why this repo was chosen
- FastAPI inherits key runtime behavior from Starlette. Starlette is the right source for conventions around app assembly, middleware ordering, lifespan, and request state.
- Valuable here because it distinguishes framework guarantees from habits that only appear in sample apps.
Repeated patterns
1) App construction keeps routing, middleware, exception handlers, and lifespan as separate inputs
starlette/applications.py:22-55defines the constructor with distinct parameters for routes, middleware, exception handlers, and lifespan.tests/test_applications.py:139-157builds one app by explicitly passing routes, exception handlers, middleware, and lifespan together.
Why chosen:
- This separation exists both in the public constructor and in the main integration-style test app.
Implication for synthesis:
- Treat app assembly concerns as separate layers; do not blur middleware, routing, error handling, and startup resources into one module-level setup blob.
2) Middleware ordering is intentional and framework-enforced
starlette/applications.py:35-40documents thatServerErrorMiddlewareis outermost andExceptionMiddlewareinnermost.starlette/applications.py:68-77shows the actual stack construction: framework error middleware, then user middleware, then framework exception middleware around the router.starlette/applications.py:98-101forbids adding middleware after the application has started.
Why chosen:
- This is stronger than a docs recommendation; it is constructor/runtime behavior.
Caveat / counterexample:
- User middleware is not the whole stack. If a synthesized convention says "middleware runs in the order you add it," that is incomplete because Starlette wraps user middleware with framework-owned layers.
Implication for synthesis:
- When discussing middleware order, mention Starlette's outer/inner framework layers explicitly.
- Avoid recommendations that rely on mutating middleware after startup.
3) Lifespan is the preferred resource boundary, and asynccontextmanager is the clearest shape
starlette/applications.py:46-48says lifespan replaceson_startup/on_shutdown; use one style or the other, not both.tests/test_applications.py:394-409shows the preferred shape: an@asynccontextmanagerlifespan marks startup beforeyieldand cleanup afteryield, with both verified by entering/exiting the client context.tests/test_applications.py:108-114defines lifespan state by yielding{"count": 1}.tests/test_applications.py:241-244verifies request handlers can consume that state viarequest.state.
Why chosen:
- This combines constructor guidance with working tests for startup/cleanup and state propagation.
Caveat / counterexample:
tests/test_applications.py:421-459still supports async/sync generator lifespan forms, but those tests are under a deprecation-warning filter. For future-facing synthesis, prefer@asynccontextmanager, not raw generator lifespans.
Implication for synthesis:
- Recommend lifespan for DB pools, clients, caches, and other app resources.
- Note that lifespan can yield structured state for handlers instead of relying on import-time globals.
Strong citation candidates
- Constructor-level separation of app assembly concerns:
starlette/applications.py:22-55 - Middleware stack ordering is framework-defined:
starlette/applications.py:68-77 - Cannot add middleware after startup:
starlette/applications.py:98-101 - Lifespan replaces startup/shutdown hooks:
starlette/applications.py:46-48 - Preferred
@asynccontextmanagerlifespan behavior:tests/test_applications.py:394-409
Pattern candidates supported by this repo
- separate routes, middleware, exception handlers, and lifespan at app assembly time
- preserve middleware ordering intentionally, including framework-owned outer/inner layers
- prefer lifespan over startup/shutdown hooks for resource management
- use lifespan-yielded state instead of import-time globals for app-scoped data