Skip to content

Schema dataclass missing $dynamicRef/$dynamicAnchor fields (JSON Schema 2020-12) #4789

Description

@aqeelat

Summary

Litestar's Schema dataclass in litestar/openapi/spec/schema.py implements an exhaustive JSON Schema 2020-12 / OAS 3.1 model — it includes prefix_items, contains, dependent_schemas, schema_if, schema_then, schema_else, unevaluated_properties, unevaluated_items, and more.

However, two 2020-12 keywords are missing: $dynamicRef and $dynamicAnchor.

These are the only applicator keywords from JSON Schema 2020-12 not yet represented in the dataclass.

Why This Matters

$dynamicRef/$dynamicAnchor enable:

  • Recursive types (category trees, org charts, file systems) without schema duplication
  • Generic/template patterns (PaginatedResponse<T>, ApiResponse<T>) — OpenAPI 3.2 explicitly recommends this pattern

These are meaningful for any spec that is hand-authored or generated with OAS 3.1's full JSON Schema 2020-12 vocabulary. Since Litestar is OAS 3.1-first, users who write or extend specs with these keywords should be able to represent them through the official Schema dataclass.

Proposed Change

Add two fields to litestar/openapi/spec/schema.py:

dynamic_ref: str | None = field(default=None, alias="$dynamicRef")
dynamic_anchor: str | None = field(default=None, alias="$dynamicAnchor")

This is analogous to the existing ref: str | None = field(default=None, alias="$ref") pattern already in the dataclass.

Example

from litestar.openapi.spec import Schema

# Template schema with an open item-type slot
paginated_template = Schema(
    defs={
        "itemType": Schema(
            dynamic_anchor="itemType",
            not_=Schema()  # forbidden default — must be overridden by dynamic scope
        )
    },
    properties={
        "items": Schema(
            type="array",
            items=Schema(dynamic_ref="#itemType")
        )
    }
)

Notes

  • This is a spec-completeness change only. It does not require changes to the schema generation layer (msgspec/pydantic plugin) — that is a separate concern.
  • $dynamicRef resolution semantics (dynamic scope walking at runtime) are not in scope for this issue.
  • The change is backward-compatible; both fields default to None.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions