Skip to content

Lifespans

Xpresso supports lifespan context managers from Starlette. This is the only way to handle startup/shutdown; there are no startup/shutdown events in Xpresso.

The main difference vs. Starlette is that the lifespan context manager is allowed to depend on "app" scoped dependencies (see Dependency Scopes), including the App itself:

from contextlib import asynccontextmanager
from typing import AsyncIterator

from pydantic import BaseModel

from xpresso import App, Path


class AppState(BaseModel):
    started: bool = False


@asynccontextmanager
async def lifespan(state: AppState) -> AsyncIterator[None]:
    state.started = True
    yield


class AppHealth(BaseModel):
    running: bool


async def healthcheck(state: AppState) -> AppHealth:
    return AppHealth(running=state.started)


app = App(
    lifespan=lifespan, routes=[Path("/health", get=healthcheck)]
)

Tip

You don't need app.state or request.state in Xpresso. Instead, you can create you own strongly typed mutable or immutable state object and inject it into your lifespan and/or endpoints like in the example above.

Router lifespans

Routers can also have lifespans, and these lifespans will be executed when the top level App's lifespan executes:

from contextlib import asynccontextmanager
from typing import AsyncIterator, List

from xpresso import App, Path, Router
from xpresso.routing.mount import Mount


class Logger(List[str]):
    pass


@asynccontextmanager
async def app_lifespan(logger: Logger) -> AsyncIterator[None]:
    logger.append("App lifespan")
    yield


@asynccontextmanager
async def router_lifespan(logger: Logger) -> AsyncIterator[None]:
    logger.append("Router lifespan")
    yield


async def get_logs(logger: Logger) -> List[str]:
    return logger


app = App(
    routes=[
        Mount(
            "",
            app=Router(
                routes=[
                    Path(
                        "/logs",
                        get=get_logs,
                    )
                ],
                lifespan=router_lifespan,
            ),
        )
    ],
    lifespan=app_lifespan,
)

Note

Only Xpresso Routers and mounted Apps support multiple lifespans. Lifespans for arbitrary mounted ASGI apps (using Mount) will not work.