Skip to content

FastAPI Helpers

This module contains the FastAPI integration layer: error handlers, content-type enforcement helpers, OpenAPI request-body helpers, and route configuration utilities.

FastAPI integration helpers for jsonpatch.

Default error mapping: - 415: Wrong Content-Type for JSON Patch (application/json-patch+json) - 422: Request validation errors (malformed JSON, invalid operationns or pointers, model revalidation failure) - 409: Patch is valid but cannot be applied to current resource state - 500: Server misconfiguration or unexpected failures (e.g., invalid registry/op classes)

JsonPatchRoute dataclass

Configure JSON Patch routes with a single source of truth.

Source code in jsonpatchx/fastapi.py
@dataclass(frozen=True)
class JsonPatchRoute:
    """Configure JSON Patch routes with a single source of truth."""

    patch_model: type[JsonPatchFor[Any, Any]]
    examples: dict[str, Any] | None = None
    strict_content_type: bool = True
    media_type: str = JSON_PATCH_MEDIA_TYPE
    request_body_overrides: dict[str, Any] | None = None
    request_param_overrides: dict[str, Any] | None = None

    def route_kwargs(self) -> dict[str, Any]:
        """Return FastAPI route kwargs for this JSON Patch contract."""
        return patch_route_kwargs(
            self.patch_model,
            examples=self.examples,
            allow_application_json=not self.strict_content_type,
            media_type=self.media_type,
            request_body_overrides=self.request_body_overrides,
        )

    def Body(self) -> BodyParam:
        """Return the configured FastAPI `Body(...)` parameter."""
        body_kwargs = dict(self.request_param_overrides or {})
        if self.strict_content_type:
            body_kwargs.setdefault("media_type", self.media_type)
        return cast(BodyParam, Body(..., **body_kwargs))

Body()

Return the configured FastAPI Body(...) parameter.

Source code in jsonpatchx/fastapi.py
def Body(self) -> BodyParam:
    """Return the configured FastAPI `Body(...)` parameter."""
    body_kwargs = dict(self.request_param_overrides or {})
    if self.strict_content_type:
        body_kwargs.setdefault("media_type", self.media_type)
    return cast(BodyParam, Body(..., **body_kwargs))

route_kwargs()

Return FastAPI route kwargs for this JSON Patch contract.

Source code in jsonpatchx/fastapi.py
def route_kwargs(self) -> dict[str, Any]:
    """Return FastAPI route kwargs for this JSON Patch contract."""
    return patch_route_kwargs(
        self.patch_model,
        examples=self.examples,
        allow_application_json=not self.strict_content_type,
        media_type=self.media_type,
        request_body_overrides=self.request_body_overrides,
    )

install_jsonpatch_error_handlers(app)

Register a FastAPI exception handler for PatchError.

Parameters:

Name Type Description Default
app FastAPI

The FastAPI application to configure.

required

Examples:

app = FastAPI()
install_jsonpatch_error_handlers(app)
Source code in jsonpatchx/fastapi.py
def install_jsonpatch_error_handlers(app: FastAPI) -> None:
    """Register a FastAPI exception handler for `PatchError`.

    Arguments:
        app: The FastAPI application to configure.

    Examples:

        app = FastAPI()
        install_jsonpatch_error_handlers(app)
    """

    @app.exception_handler(PatchError)
    def _patch_error_handler(request: Request, exc: PatchError) -> JSONResponse:
        return _patch_error_response_map(exc)

patch_content_type_dependency(enabled, *, media_type=JSON_PATCH_MEDIA_TYPE)

Return a dependency list that enforces the JSON Patch media type.

Parameters:

Name Type Description Default
enabled bool

Whether content-type enforcement should be enabled.

required
media_type str

The accepted JSON Patch media type.

JSON_PATCH_MEDIA_TYPE

Returns:

Type Description
list[Depends]

A dependency list suitable for FastAPI route decorators.

Examples:

@app.patch("/items/{item_id}", dependencies=patch_content_type_dependency(True))
def patch_item(...):
    ...
Source code in jsonpatchx/fastapi.py
def patch_content_type_dependency(
    enabled: bool, *, media_type: str = JSON_PATCH_MEDIA_TYPE
) -> list[DependsParam]:
    """Return a dependency list that enforces the JSON Patch media type.

    Arguments:
        enabled: Whether content-type enforcement should be enabled.
        media_type: The accepted JSON Patch media type.

    Returns:
        A dependency list suitable for FastAPI route decorators.

    Examples:

        @app.patch("/items/{item_id}", dependencies=patch_content_type_dependency(True))
        def patch_item(...):
            ...
    """
    if not enabled:
        return []

    def _dep(request: Request) -> None:
        _enforce_json_patch_content_type(request, media_type=media_type)

    return [Depends(_dep)]

patch_error_openapi_responses()

Return OpenAPI response schema entries for JSON Patch errors.

Returns:

Type Description
dict[int | str, dict[str, Any]]

A responses mapping suitable for FastAPI route decorators or

dict[int | str, dict[str, Any]]

openapi_extra.

Examples:

@app.patch("/items/{item_id}", responses=patch_error_openapi_responses())
def patch_item(...):
    ...
Source code in jsonpatchx/fastapi.py
def patch_error_openapi_responses() -> dict[int | str, dict[str, Any]]:
    """Return OpenAPI response schema entries for JSON Patch errors.

    Returns:
        A `responses` mapping suitable for FastAPI route decorators or
        `openapi_extra`.

    Examples:

        @app.patch("/items/{item_id}", responses=patch_error_openapi_responses())
        def patch_item(...):
            ...
    """
    patch_error_schema = {
        "type": "object",
        "properties": {
            "detail": {
                "oneOf": [
                    {"type": "string"},
                    {
                        "type": "object",
                        "properties": {
                            "index": {"type": "integer"},
                            "op": {"type": "object"},
                            "message": {"type": "string"},
                            "cause_type": {"type": ["string", "null"]},
                        },
                        "required": ["index", "op", "message"],
                    },
                ]
            }
        },
        "required": ["detail"],
    }
    validation_schema = {"$ref": "#/components/schemas/HTTPValidationError"}
    validation_or_patch_schema = {
        "oneOf": [
            patch_error_schema,
            validation_schema,
        ]
    }
    return {
        409: {
            "description": "Patch cannot be applied to current resource state",
            "content": {"application/json": {"schema": patch_error_schema}},
        },
        422: {
            "description": "Request validation or patch document validation error",
            "content": {"application/json": {"schema": validation_or_patch_schema}},
        },
        415: {
            "description": "Unsupported Media Type",
            "content": {"application/json": {"schema": patch_error_schema}},
        },
        500: {
            "description": "Patch execution error",
            "content": {"application/json": {"schema": patch_error_schema}},
        },
    }

patch_request_body(patch_model, examples=None, *, allow_application_json=False, media_type=JSON_PATCH_MEDIA_TYPE, request_body_overrides=None)

Build an OpenAPI requestBody for JSON Patch with optional examples.

Parameters:

Name Type Description Default
patch_model type[JsonPatchFor[Any, Any]]

The generated JsonPatchFor[...] model exposed in OpenAPI.

required
examples dict[str, Any] | None

Optional OpenAPI examples keyed by example name.

None
allow_application_json bool

Whether application/json should also be documented alongside the JSON Patch media type.

False
media_type str

The primary JSON Patch media type to document.

JSON_PATCH_MEDIA_TYPE
request_body_overrides dict[str, Any] | None

Optional shallow overrides for the generated top-level requestBody object. A provided content map is merged into the generated content entries.

None

Returns:

Type Description
dict[str, Any]

An openapi_extra fragment containing a requestBody entry.

Notes

JSON Patch requests should use application/json-patch+json. This library advertises only that media type by default. Set allow_application_json=True to also document application/json when you intentionally accept both.

Examples:

@app.patch(
    "/configs/{config_id}",
    openapi_extra=patch_request_body(ConfigPatch, examples={"set": {...}}),
)
def patch_config(...):
    ...

# Add an extra media type and override requestBody.required
openapi_extra=patch_request_body(
    ConfigPatch,
    request_body_overrides={
        "required": False,
        "content": {"application/merge-patch+json": {"schema": {"type": "object"}}},
    },
)
Source code in jsonpatchx/fastapi.py
def patch_request_body(
    patch_model: type[JsonPatchFor[Any, Any]],
    examples: dict[str, Any] | None = None,
    *,
    allow_application_json: bool = False,
    media_type: str = JSON_PATCH_MEDIA_TYPE,
    request_body_overrides: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Build an OpenAPI requestBody for JSON Patch with optional examples.

    Arguments:
        patch_model: The generated `JsonPatchFor[...]` model exposed in OpenAPI.
        examples: Optional OpenAPI examples keyed by example name.
        allow_application_json: Whether `application/json` should also be
            documented alongside the JSON Patch media type.
        media_type: The primary JSON Patch media type to document.
        request_body_overrides: Optional shallow overrides for the generated
            top-level `requestBody` object. A provided `content` map is merged
            into the generated content entries.

    Returns:
        An `openapi_extra` fragment containing a `requestBody` entry.

    Notes:
        JSON Patch requests should use `application/json-patch+json`. This
        library advertises only that media type by default. Set
        `allow_application_json=True` to also document `application/json` when
        you intentionally accept both.

    Examples:

        @app.patch(
            "/configs/{config_id}",
            openapi_extra=patch_request_body(ConfigPatch, examples={"set": {...}}),
        )
        def patch_config(...):
            ...

        # Add an extra media type and override requestBody.required
        openapi_extra=patch_request_body(
            ConfigPatch,
            request_body_overrides={
                "required": False,
                "content": {"application/merge-patch+json": {"schema": {"type": "object"}}},
            },
        )
    """
    schema_ref = f"#/components/schemas/{patch_model.__name__}"
    content: dict[str, Any] = {
        media_type: {"schema": {"$ref": schema_ref}},
    }
    if examples:
        content[media_type]["examples"] = examples
    if allow_application_json:
        content["application/json"] = {"schema": {"$ref": schema_ref}}
    request_body: dict[str, Any] = {"required": True, "content": content}
    if request_body_overrides:
        override_content = request_body_overrides.get("content")
        if isinstance(override_content, dict):
            content.update(override_content)
        request_body.update(
            {
                key: value
                for key, value in request_body_overrides.items()
                if key != "content"
            }
        )
    return {"requestBody": request_body}

patch_route_kwargs(patch_model=None, examples=None, *, allow_application_json=False, media_type=JSON_PATCH_MEDIA_TYPE, request_body_overrides=None)

Return FastAPI decorator kwargs that keep docs and enforcement aligned.

Parameters:

Name Type Description Default
patch_model type[JsonPatchFor[Any, Any]] | None

Optional patch model used to generate request-body docs.

None
examples dict[str, Any] | None

Optional OpenAPI examples for the patch request body.

None
allow_application_json bool

Whether application/json should be documented and accepted alongside the JSON Patch media type.

False
media_type str

The primary JSON Patch media type to document.

JSON_PATCH_MEDIA_TYPE
request_body_overrides dict[str, Any] | None

Optional shallow overrides for the generated requestBody.

None

Returns:

Type Description
dict[str, Any]

A FastAPI route kwargs mapping containing responses,

dict[str, Any]

dependencies, and optionally openapi_extra.

Notes

If allow_application_json is true, application/json is documented and content-type enforcement is disabled to allow both media types.

Source code in jsonpatchx/fastapi.py
def patch_route_kwargs(
    patch_model: type[JsonPatchFor[Any, Any]] | None = None,
    examples: dict[str, Any] | None = None,
    *,
    allow_application_json: bool = False,
    media_type: str = JSON_PATCH_MEDIA_TYPE,
    request_body_overrides: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Return FastAPI decorator kwargs that keep docs and enforcement aligned.

    Arguments:
        patch_model: Optional patch model used to generate request-body docs.
        examples: Optional OpenAPI examples for the patch request body.
        allow_application_json: Whether `application/json` should be documented
            and accepted alongside the JSON Patch media type.
        media_type: The primary JSON Patch media type to document.
        request_body_overrides: Optional shallow overrides for the generated
            `requestBody`.

    Returns:
        A FastAPI route kwargs mapping containing `responses`,
        `dependencies`, and optionally `openapi_extra`.

    Notes:
        If `allow_application_json` is true, `application/json` is documented
        and content-type enforcement is disabled to allow both media types.
    """
    kwargs: dict[str, Any] = {
        "responses": patch_error_openapi_responses(),
        "dependencies": patch_content_type_dependency(
            not allow_application_json,
            media_type=media_type,
        ),
    }
    if patch_model is not None:
        kwargs["openapi_extra"] = patch_request_body(
            patch_model,
            examples,
            allow_application_json=allow_application_json,
            media_type=media_type,
            request_body_overrides=request_body_overrides,
        )
    return kwargs