loom.rest.fastapi¶
FastAPI-specific REST layer for Loom.
Provides the FastAPI binding for compiled REST interfaces:
- MsgspecJSONResponse — zero-copy JSON response.
- bind_interfaces() — binds compiled routes to FastAPI.
- create_fastapi_app() — composition root.
- describe_fastapi_app() — application self-description.
- class loom.rest.fastapi.MsgspecJSONResponse(*args, **kwargs)[source]¶
Bases:
ResponseFastAPI
Responsesubclass that encodes content withmsgspec.json.Drop-in replacement for
fastapi.responses.JSONResponsewith two advantages:Native
msgspec.Structserialisation — nodictconversion needed.Single encoding pass — content is written directly to bytes with no intermediate JSON string or Pydantic round-trip.
- Parameters:
content – Any object supported by
msgspec.json.encode.status_code – HTTP status code. Defaults to
200.headers – Additional response headers.
media_type – Defaults to
"application/json".background – Optional Starlette background task.
args (Any)
kwargs (Any)
- Return type:
Any
Example:
return MsgspecJSONResponse(content=my_struct, status_code=201)
- loom.rest.fastapi.bind_interfaces(app, compiled_routes, factory, executor, observability_runtime)[source]¶
Register compiled routes on a FastAPI application.
For each
CompiledRoute, creates a dynamic async handler and registers it viaapp.add_api_route. Path parameters are inferred from the route path template and exposed in the handler signature so FastAPI validates and documents them correctly.Nested JSON Schema
$defsproduced by msgspec/pydantic are collected into a shared component registry and returned. The caller is responsible for injecting these intocomponents.schemasof the OpenAPI document.- Parameters:
app (fastapi.FastAPI) – FastAPI application instance to register routes on.
compiled_routes (Sequence[CompiledRoute]) – Ordered list of fully resolved routes produced by
RestInterfaceCompiler.factory (UseCaseFactory) – Use-case factory for constructing instances per request.
executor (RuntimeExecutor) – Runtime executor that drives the use-case pipeline.
observability_runtime (ObservabilityRuntime) – Shared runtime used to emit request lifecycle events around each handler execution.
- Returns:
Mapping of schema name → JSON Schema fragment for all collected
$defsthat should appear undercomponents.schemas.- Return type:
Example:
compiler = RestInterfaceCompiler(use_case_compiler) routes = compiler.compile(UserRestInterface) component_schemas = bind_interfaces( app, routes, factory, executor, observability_runtime=ObservabilityRuntime.noop(), )
- loom.rest.fastapi.create_app(*config_paths, code_path=None, metrics_registry=None, authenticator=None, resolvers=())[source]¶
Create a FastAPI application from one or more YAML config files.
Config files are merged left-to-right — later files override earlier ones. Each file may also declare a top-level
includeslist to pull in additional base files before its own values (resolved byloom.core.config.ConfigContext.from_yaml()).${secrets:...}and${ssm:...}placeholders resolve through loom’s built-in AWS resolvers unless resolvers supplies one with the same name.TraceIdMiddlewareis mounted automatically. Structured logging and OTEL come from the top-levelobservability:section. Prometheus middleware is mounted whenobservability.prometheus.enabledistrue.Authentication is mechanism-agnostic: configure
app.rest.auth.jwtfor the built-in stateless JWT mechanism, or pass authenticator for any other. Exactly one of the two may be active.The optional
sql:section wires the SQL subsystem: its connections open inside the app lifespan and two collaborators are registered in the container (null implementations raising an actionableConfigErrorwhen the section is absent).CallerBoundSqlderives the query roles from the verified caller and is what a use case serving a request should inject;SqlQueryServicetakes the roles as an argument and is for system work that has no caller. Connections opting in withsql_endpoint.enabledplus an explicitsql_endpoint.authmount aPOST /sql/{name}endpoint;auth: identityadditionally requires a configured authentication mechanism, and a non-emptyallowed_rolesalso requires that mechanism to bind roles to the verified caller identity (for JWT,app.rest.auth.jwt.roles_claim).- Parameters:
*config_paths (str) – One or more paths to YAML configuration files.
code_path (str | None) – Optional override for
app.code_path. Resolved relative to the first config file when not absolute.metrics_registry (CollectorRegistry | None) – Optional Prometheus
CollectorRegistryused forPrometheusMiddlewareand the scrape endpoint. Defaults to the global registry. Pass a freshCollectorRegistry()in tests to avoidValueError: Duplicated timeserieswhen multiple apps withobservability.prometheus.enabled: trueare created in the same process.authenticator (Authenticator | None) – Custom authentication mechanism. Mutually exclusive with the
app.rest.auth.jwtconfig section.resolvers (Sequence[ConfigResolver]) – Resolvers for
${name:key}placeholders, registered before the built-insecretsandssmdefaults. A resolver named like a default replaces it.
- Returns:
Configured
fastapi.FastAPIapplication, ready to serve.- Raises:
ConfigError – When no config path is given, or when both authenticator and
app.rest.auth.jwtare supplied.- Return type:
fastapi.FastAPI
Example — single config:
app = create_app("config/app.yaml")
Example — a mechanism of your own:
app = create_app("config/app.yaml", authenticator=MyMtlsAuthenticator())
Example — base + environment override:
app = create_app("config/base.yaml", "config/production.yaml")
Example — single file using inline includes:
# config/app.yaml # includes: # - base.yaml # - secrets.yaml app = create_app("config/app.yaml")
- loom.rest.fastapi.create_fastapi_app(result, routes=None, *, interfaces=None, observability_runtime=None, middleware=(), defaults=None, **fastapi_kwargs)[source]¶
Create a FastAPI application from a bootstrap result and REST interfaces.
Compiles all
RestInterfacedeclarations viaRestInterfaceCompiler, binds each compiled route to theFastAPIinstance, and returns the ready application.Compilation is fail-fast: any structural error (missing use-case plan, duplicate route, missing prefix) raises
InterfaceCompilationErrorbefore the app starts accepting requests.- Parameters:
result (BootstrapResult) – Fully initialised
BootstrapResultfrombootstrap_app().routes (RouteSources | None) – Which interfaces to mount and from which origin — see
RouteSources.routes.configcompiles afterroutes.python, deterministically; a(method, path)collision between the two aborts naming both — neither side takes precedence.routes.disabledis applied toroutes.pythonbefore the merge, so a disabled Python route can be redeclared inroutes.configwithout colliding with itself; an entry matching no Python-declared route aborts startup instead of doing nothing. Required unless interfaces is given instead.interfaces (Sequence[type[RestInterface[Any]]] | None) – Deprecated keyword-only alias for
routes=RouteSources(python=interfaces). Kept only so code written beforeRouteSourcesexisted keeps working when it calledcreate_fastapi_app(result, interfaces=[...])— every published example did; emits aDeprecationWarningnaming routes as the replacement. Mutually exclusive with routes.observability_runtime (ObservabilityRuntime | None) – Shared runtime used to emit lifecycle events around each request.
ASGI middleware classes to register on the application. Added in declaration order (first = outermost wrapper). Accepts any class compatible with
FastAPI.add_middleware. Example:from loom.rest.middleware import TraceIdMiddleware from loom.prometheus import PrometheusMiddleware app = create_fastapi_app( result, RouteSources(python=[...]), observability_runtime=ObservabilityRuntime.noop(), middleware=[TraceIdMiddleware, PrometheusMiddleware], )
defaults (RestApiDefaults | None) – Global REST API defaults (pagination mode, profile policy). Falls back to
RestApiDefaultswhen not provided.**fastapi_kwargs (Any) – Additional keyword arguments forwarded to the
FastAPIconstructor (e.g.title,version,docs_url).
- Returns:
Configured
fastapi.FastAPIinstance ready to serve requests.- Raises:
InterfaceCompilationError – If any interface fails structural validation.
TypeError – If neither routes nor interfaces is given, or both are.
- Return type:
fastapi.FastAPI
Example:
app = create_fastapi_app( result, RouteSources(python=[UserRestInterface, OrderRestInterface]), defaults=RestApiDefaults(pagination_mode=PaginationMode.CURSOR), observability_runtime=ObservabilityRuntime.noop(), title="My API", version="2.0.0", )
- loom.rest.fastapi.describe_fastapi_app(app)[source]¶
Describe an application built by
create_app().The document always carries the application identity under
"app", plus one section per wired pillar —"agents"when anai:section is configured. Only publishable values appear: no instructions, no model binding, no URL and no credential reference.- Parameters:
app (fastapi.FastAPI) – Application previously built by
create_app().- Returns:
JSON-encodable self-description of the application.
- Raises:
IntrospectionError – When app was not built by
create_app(), or when a pillar contribution cannot be resolved.- Return type:
Example:
app = create_app("config/app.yaml") describe_fastapi_app(app)["app"] # {'name': 'billing', 'version': '1.4.0'}