plugins/litestar/skills/litestar-app-setup/SKILL.md
Build and configure Litestar application entrypoints, app-level configuration, startup/shutdown lifecycle, lifespan context managers, application state, hooks, and layered parameter precedence. Use when creating a new Litestar service, restructuring app initialization, or setting app-level defaults. Do not use for isolated handler logic that belongs in routing, requests, responses, DTO-focused skills, or the in-process event bus handled by `litestar-events`.
npx skillsauth add alti3/litestar-skills litestar-app-setupInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill when defining or refactoring the application root object and its global behavior.
app.py, application.py, main.py, or factory) for runtime and CLI discovery.Litestar(...) with explicit route_handlers=[...] (controllers, routers, or handlers); this list is required.on_startup / on_shutdown for straightforward init/teardown hooks.lifespan=[...] async context managers for resource lifecycles that require context ownership.after_exception, before_send, on_app_init) only for cross-cutting concerns.route_handlers=[...] registration and avoid dynamic side effects during import.debug=True local-only and tie runtime behavior to environment settings.Litestar instance.route_handlers, containing controllers/routers/handlers./) 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])
Use on_startup=[...] and on_shutdown=[...] for ordered initialization and teardown:
from typing import cast
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
from litestar import Litestar
DB_URI = "postgresql+asyncpg://postgres:[email protected]: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:
litestar-events when emitting decoupled in-process side effects with app.emit(...) and listeners.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:[email protected]:5432/db")
app.state.engine = engine
try:
yield
finally:
await engine.dispose()
app = Litestar(route_handlers=[...], lifespan=[db_connection])
Shutdown ordering rule:
Use state for app-scoped shared context, not as a default data transport mechanism.
Key behaviors:
scope["litestar_app"].Litestar.from_scope(scope).request.app.state and socket.app.state expose state from connection objects.state is also injectable into handlers and dependencies by using a state kwarg.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:
ImmutableState typing to enforce no mutation (attribute assignment raises AttributeError).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))
after_exception(exception, scope).before_send(message, scope).message["type"] (for example http.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_initLitestar constructor arguments before app instantiation.AppConfig.on_app_init handlers must be synchronous (cannot be coroutine functions) because they run inside __init__.Litestar layers:
Precedence rule:
Layered parameters documented on the Applications page:
after_requestafter_responsebefore_requestcache_controldependenciesdtoetagexception_handlersguardsinclude_in_schemamiddlewareoptrequest_classresponse_classresponse_cookiesresponse_headersreturn_dtosecuritytagstype_decoderstype_encoderswebsocket_classlitestar run resolves the correct app).route_handlers registration is explicit and import-side-effect free.deep_copy behavior.state injection in handlers/dependencies uses the expected state class.after_exception is used for side effects only, not exception handling.before_send logic is message-type aware and does not mutate unintended messages.on_app_init hooks are synchronous and return AppConfig.litestar-routing for endpoint grouping and path design.litestar-events for in-process event-bus listeners and app.emit(...), not startup/shutdown ownership.litestar-lifecycle-hooks for request/response interception and cross-cutting instrumentation.litestar-dependency-injection for dependency scoping across layers.litestar-logging, litestar-middleware, and litestar-openapi for domain-specific configuration depth.development
Build Litestar WebSocket endpoints with low-level websocket handlers, websocket listeners, websocket streams, dependency injection, custom websocket classes, transport-mode control, and graceful connection lifecycle handling. Use when implementing bidirectional real-time communication, reactive websocket message handling, or proactive server push over WebSockets. Do not use for server-side pub/sub fanout that is better expressed with channels alone.
tools
Test Litestar applications with TestClient, AsyncTestClient, create_test_client, websocket test helpers, dependency overrides, mocked dependencies, lifecycle-aware fixtures, and deterministic success and failure assertions. Use when adding or fixing Litestar test coverage, including exception contracts, override precedence, websocket behavior, event-bus side effects, or live-server-only response patterns. Do not use as a substitute for production observability or runtime debugging strategy.
development
Configure Litestar templating with `TemplateConfig`, Jinja/Mako/MiniJinja engines, file-or-string `Template` responses, request and CSRF-aware context, template callables, and custom engine integration. Use when implementing or fixing server-rendered HTML in Litestar. Do not use for static asset serving or pure JSON API endpoints.
development
Configure Litestar stores and the store registry for caching, server-side sessions, rate limiting, and other key-value state with explicit backend selection, bytes-safe data handling, TTL and renewal policy, namespacing, registry wiring, and lifecycle cleanup. Use when a Litestar app depends on `MemoryStore`, `FileStore`, `RedisStore`, `ValkeyStore`, or `StoreRegistry`. Do not use for relational persistence, domain repositories, or response-caching policy details that belong in database or caching-focused skills.