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 the router handler function |
|
After the route handler function |
|
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