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.

Tip

Lifespan dependencies are automatically assigned the "app" scope, you don't need to explicitly set it.

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.