Skip to main content

Data Schemas and Validation

FastAPI generates OpenAPI documentation by translating Python type hints and Pydantic models into formal OpenAPI objects. This process relies on a set of Pydantic models defined in fastapi/openapi/models.py that strictly follow the OpenAPI 3.1.0 specification, which in turn adopts the JSON Schema 2020-12 standard for data modeling.

JSON Schema 2020-12 and the Schema Class

The Schema class in fastapi.openapi.models is the primary structure used to represent data types, validation rules, and relationships between models. Because it implements JSON Schema 2020-12, it supports advanced features like multiple types for a single field (e.g., a field being both a string and null).

Field Aliases and Keywords

Many JSON Schema keywords are reserved words in Python (like type, format, or $ref). FastAPI handles this by using field aliases in the Schema model. When working with the Schema class directly, you use the Python-friendly attribute names:

  • schema_ maps to $schema
  • ref maps to $ref
  • defs maps to $defs
  • not_ maps to not
  • if_ maps to if
  • else_ maps to else

Type Flexibility

In JSON Schema 2020-12, the type field can be a single string or an array of strings. FastAPI's Schema model reflects this by defining type as SchemaType | list[SchemaType] | None. This is verified in tests/test_openapi_schema_type.py:

from fastapi.openapi.models import Schema

def test_allowed_schema_type():
# Test that Schema accepts a single string
schema = Schema(type="array")
assert schema.type == "array"

# Test that Schema accepts a list of strings (JSON Schema 2020-12)
schema_multi = Schema(type=["string", "null"])
assert schema_multi.type == ["string", "null"]

Polymorphic Schemas and Discriminators

When a path operation accepts or returns a Union of different Pydantic models, FastAPI uses the Discriminator class to help client generators identify which specific model is being used.

The Discriminator model contains two key fields:

  1. propertyName: The name of the field in the payload used to distinguish between types.
  2. mapping: An optional dictionary mapping field values to schema references.

In tests/test_union_body_discriminator.py, FastAPI demonstrates how a Union with a Pydantic Field(discriminator="...") results in an OpenAPI oneOf schema combined with a discriminator object:

"schema": {
"oneOf": [
{"$ref": "#/components/schemas/FirstItem"},
{"$ref": "#/components/schemas/OtherItem"}
],
"discriminator": {
"propertyName": "value",
"mapping": {
"first": "#/components/schemas/FirstItem",
"other": "#/components/schemas/OtherItem"
}
},
"title": "Item"
}

XML Metadata Customization

While FastAPI is primarily used with JSON, the OpenAPI specification allows for XML serialization metadata. The XML class in fastapi.openapi.models provides fields to control how a schema is represented in XML:

  • name: Replaces the name of the element.
  • namespace: Sets the XML namespace.
  • prefix: Sets the XML prefix.
  • attribute: If True, the property is serialized as an XML attribute instead of an element.
  • wrapped: If True, an array is wrapped in an additional element.

These attributes are attached to the Schema object via the xml field.

Internal Schema Generation

The actual conversion from Python types to these Schema models is orchestrated in fastapi/openapi/utils.py. The function get_openapi serves as the entry point, which calls get_fields_from_routes to collect all models used in the application.

FastAPI uses get_schema_from_model_field (imported from fastapi._compat) to extract the JSON Schema representation from Pydantic models. These raw dictionaries are then integrated into the final OpenAPI model, which includes the Components and Paths that hold the Schema definitions.

One notable transition in FastAPI's implementation is the deprecation of the example field in favor of examples, following the OpenAPI 3.1.0 spec. The Schema class maintains the example field for backward compatibility but marks it as deprecated:

class Schema(BaseModelWithConfig):
# ...
examples: list[Any] | None = None
example: Annotated[
Any | None,
typing_deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead."
),
] = None