Skip to content

Integration Backend

The interface a framework adapter implements to wire pyjinhx into an app, and the per-request seam that carries request state into render().

PyJinHx ships one adapter, pyjinhx.integrations.fastapi, and this interface is what a Flask, bare-WSGI, or other adapter would implement to plug in the same way.

Internal modules — for adapter authors

Nothing on this page is part of the public API: IntegrationBackend, register_backend, request_scope and get_load_context are all absent from pyjinhx.__all__. App code does not need any of it — setup(app, context_factory=...) is the public seam, and components read context through their load()'s annotated ctx parameter (see Reactivity → Load context).

IntegrationBackend

class IntegrationBackend(Protocol):
    def is_installed(self, app: object) -> bool: ...
    def mark_installed(self, app: object) -> None: ...
    def mount_static(self, app: object, directory: str) -> None: ...
    def on_startup(self, app: object) -> None: ...
    def on_shutdown(self, app: object) -> None: ...
    def to_response(self, result: object, request: object | None) -> object: ...

What a framework adapter provides so setup() can wire pyjinhx in.

Method Purpose
is_installed(app) Whether setup has already been applied to app, so a re-entrant setup() doesn't stack two scopes or two lifespans on one request.
mark_installed(app) Record that setup has been applied to app.
mount_static(app, directory) Serve the files in directory at /static on app.
on_startup(app) Run pyjinhx's configure step as app starts.
on_shutdown(app) Run pyjinhx's shutdown step as app stops.
to_response(result, request) Adapt a pjx handler return into the framework's response type, by emitting what pyjinhx.responses.compose() answers. A result compose() answers PASSTHROUGH for is the framework's own to keep — with one exception: a response whose status_code is 300-399 and which carries a Location header is translated to 204 + HX-Redirect when the request carries HX-Request (see Response composition).

Route adaptation is deliberately absent from this interface — turning a handler's pjx return into a framework response is to_response(), but wiring that onto routes differs enough per framework (FastAPI swaps APIRoute subclasses, Flask would use an after_request hook) that each backend owns its own wiring.

register_backend / get_backend

def register_backend(backend: IntegrationBackend) -> None
def get_backend() -> IntegrationBackend | None

register_backend() publishes the adapter that setup(app=...) dispatches through — one slot, since a process wires pyjinhx into one app. get_backend() returns the registered adapter, or None when no adapter module was imported.

Request-scoped load context

A backend binds one request_scope(load_context=...) (from pyjinhx.session) per request around its handler. The load_context is whatever the app's context_factory derives from the framework's native request object — headers, auth info, whatever a component's load() needs.

from pyjinhx.integrations.base import load_context_for

load_context = load_context_for(request, context_factory)
with request_scope(load_context=load_context) as session:
    ...

Inside the scope, get_load_context() returns that value for the life of the request:

from typing import Self

from pyjinhx import ReactiveComponent
from pyjinhx.session import get_load_context


class RequestScoped(ReactiveComponent):
    @classmethod
    def load(cls) -> Self:
        request = get_load_context()
        return cls(...)

Non-FastAPI frameworks

Implement IntegrationBackend for your framework and call register_backend() with an instance, mirroring pyjinhx.integrations.fastapi. See the FastAPI integration source for a complete example of wiring request_scope(), header parsing, and to_response().