Life Cycle Hooks#

Life cycle hooks allow the execution of a callable at a certain point during the request-response cycle. The hooks available are:

Name

Runs

before_request

Before the router handler function

after_request

After the route handler function

after_response

After the response has been sent

Before Request#

The before_request hook runs immediately before calling the route handler function. It can be any callable accepting a Request as its first parameter and returns either None or a value that can be used in a response. If a value is returned, the router handler for this request will be bypassed.

from typing import Dict, Optional

from starlite import Request, Starlite, get


async def before_request_handler(request: Request) -> Optional[Dict[str, str]]:
    name = request.query_params["name"]
    if name == "Ben":
        return {"message": "These are not the bytes you are looking for"}
    request.state["message"] = f"Use the handler, {name}"
    return None


@get("/")
async def handler(request: Request, name: str) -> Dict[str, str]:
    message: str = request.state["message"]
    return {"message": message}


app = Starlite(route_handlers=[handler], before_request=before_request_handler)

# run: /?name=Luke
# run: /?name=Ben
from typing import Optional

from starlite import Request, Starlite, get


async def before_request_handler(request: Request) -> Optional[dict[str, str]]:
    name = request.query_params["name"]
    if name == "Ben":
        return {"message": "These are not the bytes you are looking for"}
    request.state["message"] = f"Use the handler, {name}"
    return None


@get("/")
async def handler(request: Request, name: str) -> dict[str, str]:
    message: str = request.state["message"]
    return {"message": message}


app = Starlite(route_handlers=[handler], before_request=before_request_handler)

# run: /?name=Luke
# run: /?name=Ben
from starlite import Request, Starlite, get


async def before_request_handler(request: Request) -> dict[str, str] | None:
    name = request.query_params["name"]
    if name == "Ben":
        return {"message": "These are not the bytes you are looking for"}
    request.state["message"] = f"Use the handler, {name}"
    return None


@get("/")
async def handler(request: Request, name: str) -> dict[str, str]:
    message: str = request.state["message"]
    return {"message": message}


app = Starlite(route_handlers=[handler], before_request=before_request_handler)

# run: /?name=Luke
# run: /?name=Ben

After Request#

The after_request hook runs after the route handler returned and the response object has been resolved. It can be any callable which takes a Response instance as its first parameter, and returns a Response instance. The Response instance returned does not necessarily have to be the one that was received.

from typing import Dict

from starlite import MediaType, Response, Starlite, get


async def after_request(response: Response) -> Response:
    if response.media_type == MediaType.TEXT:
        return Response(
            {"message": response.body.decode()},
            media_type=MediaType.JSON,
        )
    return response


@get("/hello")
async def hello() -> str:
    return "Hello, world"


@get("/goodbye")
async def goodbye() -> Dict[str, str]:
    return {"message": "Goodbye"}


app = Starlite(route_handlers=[hello, goodbye], after_request=after_request)


# run: /hello
# run: /goodbye
from starlite import MediaType, Response, Starlite, get


async def after_request(response: Response) -> Response:
    if response.media_type == MediaType.TEXT:
        return Response(
            {"message": response.body.decode()},
            media_type=MediaType.JSON,
        )
    return response


@get("/hello")
async def hello() -> str:
    return "Hello, world"


@get("/goodbye")
async def goodbye() -> dict[str, str]:
    return {"message": "Goodbye"}


app = Starlite(route_handlers=[hello, goodbye], after_request=after_request)


# run: /hello
# run: /goodbye

After Response#

The after_response hook runs after the response has been returned by the server. It can be any callable accepting a Request as its first parameter and does not return any value.

This hook is meant for data post-processing, transmission of data to third party services, gathering of metrics etc.

from collections import defaultdict
from typing import Dict

from starlite import Request, Starlite, get

COUNTER: Dict[str, int] = defaultdict(int)


async def after_response(request: Request) -> None:
    COUNTER[request.url.path] += 1


@get("/hello")
async def hello() -> Dict[str, int]:
    return COUNTER


app = Starlite(route_handlers=[hello], after_response=after_response)


# run: /hello
# run: /hello
from collections import defaultdict

from starlite import Request, Starlite, get

COUNTER: dict[str, int] = defaultdict(int)


async def after_response(request: Request) -> None:
    COUNTER[request.url.path] += 1


@get("/hello")
async def hello() -> dict[str, int]:
    return COUNTER


app = Starlite(route_handlers=[hello], after_response=after_response)


# run: /hello
# run: /hello

Note

Since the request has already been returned by the time the after_response is called, the updated state of COUNTER is not reflected in the response.

Layered hooks#

Layered architecture

Life cycle hooks are part of Starlite’s layered architecture, which means you can set them on every layer of the application. If you set hooks on multiple layers, the layer closest to the route handler will take precedence.

You can read more about this here: Layered architecture

from starlite import Response, Starlite, get


def after_request_app(response: Response) -> Response:
    return Response(content=b"app after request")


def after_request_handler(response: Response) -> Response:
    return Response(content=b"handler after request")


@get("/")
async def handler() -> str:
    return "hello, world"


@get("/override", after_request=after_request_handler)
async def handler_with_override() -> str:
    return "hello, world"


app = Starlite(
    route_handlers=[handler, handler_with_override],
    after_request=after_request_app,
)


# run: /
# run: /override