Starlette
- Start here: Frameworks
- The errors: Error Responses
Everything here is pure ASGI, so it works on a plain Starlette app and on
anything built from one. A FastAPI app reaches the same names through
FastAPI, which adds the OpenAPI schema and the health router.
GrelmicroMiddleware lives in App and IdempotencyMiddleware in
HTTP, since neither is tied to a framework.
Prefer the polymorphic micro.install(app), which detects the framework and
calls install for you.
grelmicro.integrations.starlette
Starlette integration: the lifespan, the binding, and the error responses.
Everything here is pure ASGI, so it works on a plain Starlette app and on
anything built from one. grelmicro.integrations.fastapi builds on it and
adds what only FastAPI has, an OpenAPI schema and a health router.
install
install(
app: Starlette,
micro: Grelmicro,
*,
ambient: bool = True,
) -> None
Wire micro into a Starlette app.
Chains async with micro: around the app's existing lifespan, so any
lifespan already passed to the framework keeps running and the components
are open before the first request. When ambient is True, adds
GrelmicroMiddleware so patterns resolve through Grelmicro.current()
inside request handlers, and keeps it outside every other middleware
however they were added, so one that resolves a backend ambiently, such
as IdempotencyMiddleware, always runs inside the request scope. The
placement is read back on startup and raises AmbientBindingError if it
did not hold.
Prefer the polymorphic micro.install(app), which detects the framework
and calls this for you.
| PARAMETER | DESCRIPTION |
|---|---|
app
|
The Starlette application to wire.
TYPE:
|
micro
|
The
TYPE:
|
ambient
|
Add
TYPE:
|
install_error_responses
install_error_responses(
app: Starlette, errors: ErrorResponses
) -> None
Render grelmicro rejections in a standard format on a Starlette app.
Registers one exception handler per rejection grelmicro raises to turn a caller away, and reshapes the framework's own errors into the same format, so the whole API answers in one shape.
Starlette looks a handler up through the raised exception's class
hierarchy, so registering AdmissionError covers every rejection under
it, including one a later release adds.
micro.install(app) calls this when ErrorResponses() is registered.
Call it directly only on an app that never goes through install.
Read more in the Error Responses docs.
| PARAMETER | DESCRIPTION |
|---|---|
app
|
The Starlette application to wire.
TYPE:
|
errors
|
The registered component that renders each rejection.
TYPE:
|
install_middleware
install_middleware(
app: Starlette, components: Sequence[Any]
) -> None
Add the ASGI middleware each registered component asks for.
A component that carries asgi_middleware() returns the middleware
class and the arguments to build it with, and this adds it to the app.
Registration order is wrapping order among them, so the first one
registered answers first.
A middleware that may answer a request goes innermost, behind whatever
the app added itself, so authentication, CORS and the rest run before
one of ours can answer on its own. One that only watches, an access
log, goes the other way, outside the app's own, so a request an outer
layer refuses is still seen. A component says which it is with
asgi_observes.
Both run inside GrelmicroMiddleware, which stays outermost, so one
that resolves a backend ambiently finds the app bound.
micro.install(app) calls this with the components it found, so a
direct call is only for an app that never goes through install.
| PARAMETER | DESCRIPTION |
|---|---|
app
|
The Starlette application to wire.
TYPE:
|
components
|
The registered components that carry an ASGI middleware.
TYPE:
|
is_bound
is_bound(app: Starlette) -> bool
Return whether install added the per-request binding middleware.
Called by Grelmicro.check_ambient_binding and Grelmicro.describe to
catch an app that never had micro.install(app) called on it, including
a mounted sub-application, which otherwise resolves against the host's
components with nothing reporting it.
| PARAMETER | DESCRIPTION |
|---|---|
app
|
The Starlette or FastAPI application to inspect.
TYPE:
|
error_response
error_response(
request: Request,
*,
status: int,
detail: str | None = None,
extensions: dict[str, Any] | None = None,
) -> Response
Answer from your own exception handler in the app's error format.
Writing a handler of your own is how one error opts out of the shape grelmicro installs. This is for the other case: you want your own handler and the same shape as everything else.
from grelmicro.integrations.fastapi import error_response
@app.exception_handler(InsufficientFunds)
async def handle(request: Request, exc: InsufficientFunds) -> Response:
return error_response(
request,
status=409,
detail="The account does not hold enough to cover this charge.",
extensions={"balance": exc.balance},
)
The format is read from the app, so a service that registered
ErrorResponses.tmf() answers in TMF from here too, with no second
place to keep in step. An app that registered nothing gets RFC 9457.
| PARAMETER | DESCRIPTION |
|---|---|
request
|
The request being answered, which knows its app.
TYPE:
|
status
|
HTTP status code of the response.
TYPE:
|
detail
|
Explanation of this occurrence, safe to show a client.
TYPE:
|
extensions
|
Extra members to carry, where the format has room for them.
TYPE:
|