litestar-app-setup
SKILL.md
App Setup
Use this skill when defining or refactoring the application root object and its global behavior.
Execution Workflow
- Define an importable application entrypoint (
app.py,application.py,main.py, or factory) for runtime and CLI discovery. - Construct
Litestar(...)with explicitroute_handlers=[...](controllers, routers, or handlers); this list is required. - Configure app-level concerns intentionally (dependencies, middleware, exception handlers, DTO defaults, request/response classes).
- Choose lifecycle model:
- Use
on_startup/on_shutdownfor straightforward init/teardown hooks. - Use
lifespan=[...]async context managers for resource lifecycles that require context ownership. - Add application hooks (
after_exception,before_send,on_app_init) only for cross-cutting concerns. - Use application state sparingly, initialize it explicitly, and inject it intentionally where needed.
- Validate layered overrides to ensure the closest layer to the handler wins as expected.
Implementation Rules
- Keep the app module focused on composition, not business logic.
- Prefer explicit
route_handlers=[...]registration and avoid dynamic side effects during import. - Keep lifecycle ownership deterministic; avoid mixing competing init/teardown patterns without clear intent.
- Keep state minimal and stable; avoid mutable global state unless no cleaner scope exists.
- Use application hooks for instrumentation and cross-cutting behavior, not domain logic.
- Keep
debug=Truelocal-only and tie runtime behavior to environment settings.
Application Object Fundamentals
- The root of a Litestar service is a
Litestarinstance. - The only required constructor argument is
route_handlers, containing controllers/routers/handlers. - The application object is the root layer (base path
/) and owns app-wide defaults.
from litestar import Litestar, get
@get("/")
async def hello() -> dict[str, str]:
return {"hello": "world"}
app = Litestar(route_handlers=[hello])
Startup and Shutdown Hooks
Use on_startup=[...] and on_shutdown=[...] for ordered initialization and teardown:
- Hooks can be sync or async callables.
- Startup hooks run on ASGI startup event.
- Shutdown hooks run on ASGI shutdown event.
- A common pattern is creating resources in startup and disposing them in shutdown.
from typing import cast
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
from litestar import Litestar
DB_URI = "postgresql+asyncpg://postgres:mysecretpassword@pg.db:5432/db"
def get_db_connection(app: Litestar) -> AsyncEngine:
if not getattr(app.state, "engine", None):
app.state.engine = create_async_engine(DB_URI)
return cast("AsyncEngine", app.state.engine)
async def close_db_connection(app: Litestar) -> None:
if getattr(app.state, "engine", None):
await cast("AsyncEngine", app.state.engine).dispose()
app = Litestar(on_startup=[get_db_connection], on_shutdown=[close_db_connection], route_handlers=[...])
Boundary note:
- These hooks manage app lifecycle resources.
- They are not Litestar's event bus. Use
litestar-eventswhen emitting decoupled in-process side effects withapp.emit(...)and listeners.
Lifespan Context Managers
Use lifespan=[async_context_manager, ...] when resources need a continuous owned context:
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from sqlalchemy.ext.asyncio import create_async_engine
from litestar import Litestar
@asynccontextmanager
async def db_connection(app: Litestar) -> AsyncGenerator[None, None]:
engine = getattr(app.state, "engine", None)
if engine is None:
engine = create_async_engine("postgresql+asyncpg://postgres:mysecretpassword@pg.db:5432/db")
app.state.engine = engine
try:
yield
finally:
await engine.dispose()
app = Litestar(route_handlers=[...], lifespan=[db_connection])
Shutdown ordering rule:
- If multiple lifespan context managers and shutdown hooks are registered, context managers unwind in reverse order before shutdown hooks execute in their declared order.
Application State
Use state for app-scoped shared context, not as a default data transport mechanism.
Key behaviors:
- App instance is available in ASGI scope as
scope["litestar_app"]. - You can access app from scope using
Litestar.from_scope(scope). request.app.stateandsocket.app.stateexpose state from connection objects.stateis also injectable into handlers and dependencies by using astatekwarg.
Initialization options:
State({...})from dict.State(existing_state_or_immutable_state).State([(key, value), ...]).State(..., deep_copy=True)to protect against external mutation.
Immutability option:
- Use
ImmutableStatetyping to enforce no mutation (attribute assignment raisesAttributeError).
from litestar import Litestar, get
from litestar.datastructures import State
@get("/")
def handler(state: State) -> dict[str, int]:
return {"count": state.count}
app = Litestar(route_handlers=[handler], state=State({"count": 100}, deep_copy=True))
Application Hooks
after_exception
- Called after an exception with
(exception, scope). - Intended for side effects such as metrics/logging.
- Not an exception handler replacement.
before_send
- Called on each ASGI message with
(message, scope). - Gate logic by
message["type"](for examplehttp.response.start) when mutating headers or metadata.
from litestar import Litestar, get
from litestar.datastructures import MutableScopeHeaders
@get("/test")
def handler() -> dict[str, str]:
return {"key": "value"}
async def before_send_hook_handler(message: dict, scope: dict) -> None:
if message["type"] == "http.response.start":
headers = MutableScopeHeaders.from_message(message=message)
headers["My-Header"] = Litestar.from_scope(scope).state.message
def on_startup(app: Litestar) -> None:
app.state.message = "value injected during send"
app = Litestar(route_handlers=[handler], on_startup=[on_startup], before_send=[before_send_hook_handler])
on_app_init
- Intercepts
Litestarconstructor arguments before app instantiation. - Each hook receives and must return
AppConfig. - Useful for reusable configuration packages and third-party app configuration systems.
on_app_inithandlers must be synchronous (cannot be coroutine functions) because they run inside__init__.
Layered Architecture and Precedence
Litestar layers:
- Application
- Router
- Controller
- Handler
Precedence rule:
- For layered parameters, the value set on the layer closest to the handler wins.
Layered parameters documented on the Applications page:
after_requestafter_responsebefore_requestcache_controldependenciesdtoetagexception_handlersguardsinclude_in_schemamiddlewareoptrequest_classresponse_classresponse_cookiesresponse_headersreturn_dtosecuritytagstype_decoderstype_encoderswebsocket_class
Validation Checklist
- Confirm CLI autodiscovery works (
litestar runresolves the correct app). - Confirm
route_handlersregistration is explicit and import-side-effect free. - Confirm startup/shutdown hooks run in expected order and release external resources.
- Confirm lifespan context managers dispose resources and unwind correctly.
- Confirm mixed lifespan + shutdown sequences match reverse-context + declared-hook ordering.
- Confirm state initialization uses the intended source and optional
deep_copybehavior. - Confirm
stateinjection in handlers/dependencies uses the expected state class. - Confirm
after_exceptionis used for side effects only, not exception handling. - Confirm
before_sendlogic is message-type aware and does not mutate unintended messages. - Confirm
on_app_inithooks are synchronous and returnAppConfig. - Confirm layered overrides behave as expected (closest layer wins) for app/router/controller/handler.
Cross-Skill Handoffs
- Use
litestar-routingfor endpoint grouping and path design. - Use
litestar-eventsfor in-process event-bus listeners andapp.emit(...), not startup/shutdown ownership. - Use
litestar-lifecycle-hooksfor request/response interception and cross-cutting instrumentation. - Use
litestar-dependency-injectionfor dependency scoping across layers. - Use
litestar-logging,litestar-middleware, andlitestar-openapifor domain-specific configuration depth.
Litestar References
Weekly Installs
10
Repository
alti3/litestar-skillsGitHub Stars
5
First Seen
12 days ago
Security Audits
Installed on
cline10
github-copilot10
codex10
kimi-cli10
gemini-cli10
cursor10