Configuring schema generation#

OpenAPI schema generation is enabled by default. To configure it you can pass an instance of OpenAPIConfig to the Litestar class using the openapi_config kwarg:

from litestar import Litestar
from litestar.openapi import OpenAPIConfig

app = Litestar(
    route_handlers=[...], openapi_config=OpenAPIConfig(title="My API", version="1.0.0")
)

Disabling schema generation#

If you wish to disable schema generation and not include the schema endpoints in your API, simply pass None as the value for openapi_config:

from litestar import Litestar

app = Litestar(route_handlers=[...], openapi_config=None)

Configuring schema generation on a route handler#

By default, an operation schema is generated for all route handlers. You can omit a route handler from the schema by setting include_in_schema=False:

from litestar import get


@get(path="/some-path", include_in_schema=False)
def my_route_handler() -> None: ...

You can also modify the generated schema for the route handler using the following kwargs:

tags

A list of strings that correlate to the tag specification.

security:

A list of dictionaries that correlate to the security requirements specification. The values for this key are string keyed dictionaries with the values being a list of objects.

summary

Text used for the route’s schema summary section.

description

Text used for the route’s schema description section.

response_description

Text used for the route’s response schema description section.

operation_class

A subclass of Operation which can be used to fully customize the operation object for the handler.

operation_id

A string or callable that returns a string, which servers as an identifier used for the route’s schema operationId.

deprecated

A boolean dictating whether this route should be marked as deprecated in the OpenAPI schema. Defaults to False.

raises

A list of exception classes extending from litestar.HttpException. This list should describe all exceptions raised within the route handler’s function/method. The Litestar ValidationException will be added automatically for the schema if any validation is involved (e.g. there are parameters specified in the method/function). For custom exceptions, a detail class property should be defined, which will be integrated into the OpenAPI schema. If detail isn’t specified and the exception’s status code matches one from stdlib status code, a generic message will be applied.

responses

A dictionary of additional status codes and a description of their expected content. The expected content should be based on a Pydantic model describing its structure. It can also include a description and the expected media type. For example:

Note

operation_id will be prefixed with the method name when function is decorated with HTTPRouteHandler and multiple http_method. Will also be prefixed with path strings used in Routers and Controllers to make sure id is unique.

from datetime import datetime
from typing import Optional

from pydantic import BaseModel

from litestar import get
from litestar.openapi.datastructures import ResponseSpec


class Item(BaseModel): ...


class ItemNotFound(BaseModel):
    was_removed: bool
    removed_at: Optional[datetime]


@get(
    path="/items/{pk:int}",
    responses={
        404: ResponseSpec(
            data_container=ItemNotFound, description="Item was removed or not found"
        )
    },
)
def retrieve_item(pk: int) -> Item: ...
from datetime import datetime

from pydantic import BaseModel

from litestar import get
from litestar.openapi.datastructures import ResponseSpec


class Item(BaseModel): ...


class ItemNotFound(BaseModel):
    was_removed: bool
    removed_at: datetime | None


@get(
    path="/items/{pk:int}",
    responses={
        404: ResponseSpec(
            data_container=ItemNotFound, description="Item was removed or not found"
        )
    },
)
def retrieve_item(pk: int) -> Item: ...

You can also specify security and tags on higher level of the application, e.g. on a controller, router, or the app instance itself. For example:

from litestar import Litestar, get
from litestar.openapi import OpenAPIConfig
from litestar.openapi.spec import Components, SecurityScheme, Tag


@get(
    "/public",
    tags=["public"],
    security=[{}],  # this endpoint is marked as having optional security
)
def public_path_handler() -> dict[str, str]:
    return {"hello": "world"}


@get("/other", tags=["internal"], security=[{"apiKey": []}])
def internal_path_handler() -> None: ...


app = Litestar(
    route_handlers=[public_path_handler, internal_path_handler],
    openapi_config=OpenAPIConfig(
        title="my api",
        version="1.0.0",
        tags=[
            Tag(name="public", description="This endpoint is for external users"),
            Tag(name="internal", description="This endpoint is for internal users"),
        ],
        security=[{"BearerToken": []}],
        components=Components(
            security_schemes={
                "BearerToken": SecurityScheme(
                    type="http",
                    scheme="bearer",
                )
            },
        ),
    ),
)

Accessing the OpenAPI schema in code#

The OpenAPI schema is generated during the Litestar app’s init method. Once init is finished, its accessible as app.openapi_schema. As such you can always access it inside route handlers, dependencies, etc. by access the request instance:

from litestar import Request, get


@get(path="/")
def my_route_handler(request: Request) -> dict:
    schema = request.app.openapi_schema
    return schema.dict()

Customizing Pydantic model schemas#

You can customize the OpenAPI schemas generated for pydantic models by following the guidelines in the Pydantic docs.

Additionally, you can affect how pydantic models are translated into OpenAPI components by settings a special dunder attribute on the model called __schema_name__:

Customize Components Example#
from uuid import UUID, uuid4

from pydantic import BaseModel

from litestar import Litestar, get


class IdModel(BaseModel):
    __schema_name__ = "IdContainer"

    id: UUID


@get("/id", sync_to_thread=False)
def retrieve_id_handler() -> IdModel:
    """

    Returns: An IdModel

    """
    return IdModel(id=uuid4())


app = Litestar(route_handlers=[retrieve_id_handler])

The above will result in an OpenAPI schema object that looks like this:

{
    "openapi": "3.1.0",
    "info": {"title": "Litestar API", "version": "1.0.0"},
    "servers": [{"url": "/"}],
    "paths": {
        "/id": {
            "get": {
                "operationId": "Retrieve Id Handler",
                "responses": {
                    "200": {
                        "description": "Request fulfilled, document follows",
                        "headers": {},
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/IdContainer"
                                }
                            }
                        }
                    }
                },
                "deprecated": false
            }
        }
    },
    "components": {
        "schemas": {
            "IdContainer": {
                "properties": {
                    "id": {"type": "string", "format": "uuid", "title": "Id"}
                },
                "type": "object",
                "required": ["id"],
                "title": "IdContainer"
            }
        }
    }
}

Attention

If you use multiple pydantic models that use the same name in the schema, you will need to use the __schema_name__ dunder to ensure each has a unique name in the schema, otherwise the schema components will be ambivalent.

Customizing Operation class#

You can customize the operation object used for a path in the generated OpenAPI schemas by creating a subclass of Operation.

This option can be helpful in situations where request data needs to be manually parsed as Litestar will not know how to create the OpenAPI operation data by default.

Customize Components Example#
from dataclasses import dataclass, field
from typing import Dict, List, Optional

from litestar import Litestar, MediaType, Request, post
from litestar.exceptions import HTTPException
from litestar.openapi.spec import OpenAPIMediaType, OpenAPIType, Operation, RequestBody, Schema
from litestar.status_codes import HTTP_400_BAD_REQUEST


@dataclass
class CustomOperation(Operation):
    """Custom Operation class which includes a non-standard field which is part of an OpenAPI extension."""

    x_code_samples: Optional[List[Dict[str, str]]] = field(default=None, metadata={"alias": "x-codeSamples"})

    def __post_init__(self) -> None:
        self.tags = ["ok"]
        self.description = "Requires OK, Returns OK"
        self.request_body = RequestBody(
            content={
                "text": OpenAPIMediaType(
                    schema=Schema(
                        title="Body",
                        type=OpenAPIType.STRING,
                        example="OK",
                    )
                ),
            },
            description="OK is the only accepted value",
        )
        self.x_codeSamples = [
            {"lang": "Python", "source": "import requests; requests.get('localhost/example')", "label": "Python"},
            {"lang": "cURL", "source": "curl -XGET localhost/example", "label": "curl"},
        ]


@post("/", operation_class=CustomOperation, media_type=MediaType.TEXT)
async def route(request: Request) -> str:
    """

    Returns: OK

    """

    if (await request.body()) == b"OK":
        return "OK"
    raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="request payload must be OK")


app = Litestar(route_handlers=[route])
Customize Components Example#
from dataclasses import dataclass, field
from typing import Optional

from litestar import Litestar, MediaType, Request, post
from litestar.exceptions import HTTPException
from litestar.openapi.spec import OpenAPIMediaType, OpenAPIType, Operation, RequestBody, Schema
from litestar.status_codes import HTTP_400_BAD_REQUEST


@dataclass
class CustomOperation(Operation):
    """Custom Operation class which includes a non-standard field which is part of an OpenAPI extension."""

    x_code_samples: Optional[list[dict[str, str]]] = field(default=None, metadata={"alias": "x-codeSamples"})

    def __post_init__(self) -> None:
        self.tags = ["ok"]
        self.description = "Requires OK, Returns OK"
        self.request_body = RequestBody(
            content={
                "text": OpenAPIMediaType(
                    schema=Schema(
                        title="Body",
                        type=OpenAPIType.STRING,
                        example="OK",
                    )
                ),
            },
            description="OK is the only accepted value",
        )
        self.x_codeSamples = [
            {"lang": "Python", "source": "import requests; requests.get('localhost/example')", "label": "Python"},
            {"lang": "cURL", "source": "curl -XGET localhost/example", "label": "curl"},
        ]


@post("/", operation_class=CustomOperation, media_type=MediaType.TEXT)
async def route(request: Request) -> str:
    """

    Returns: OK

    """

    if (await request.body()) == b"OK":
        return "OK"
    raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="request payload must be OK")


app = Litestar(route_handlers=[route])
Customize Components Example#
from dataclasses import dataclass, field

from litestar import Litestar, MediaType, Request, post
from litestar.exceptions import HTTPException
from litestar.openapi.spec import OpenAPIMediaType, OpenAPIType, Operation, RequestBody, Schema
from litestar.status_codes import HTTP_400_BAD_REQUEST


@dataclass
class CustomOperation(Operation):
    """Custom Operation class which includes a non-standard field which is part of an OpenAPI extension."""

    x_code_samples: list[dict[str, str]] | None = field(default=None, metadata={"alias": "x-codeSamples"})

    def __post_init__(self) -> None:
        self.tags = ["ok"]
        self.description = "Requires OK, Returns OK"
        self.request_body = RequestBody(
            content={
                "text": OpenAPIMediaType(
                    schema=Schema(
                        title="Body",
                        type=OpenAPIType.STRING,
                        example="OK",
                    )
                ),
            },
            description="OK is the only accepted value",
        )
        self.x_codeSamples = [
            {"lang": "Python", "source": "import requests; requests.get('localhost/example')", "label": "Python"},
            {"lang": "cURL", "source": "curl -XGET localhost/example", "label": "curl"},
        ]


@post("/", operation_class=CustomOperation, media_type=MediaType.TEXT)
async def route(request: Request) -> str:
    """

    Returns: OK

    """

    if (await request.body()) == b"OK":
        return "OK"
    raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="request payload must be OK")


app = Litestar(route_handlers=[route])

The above example will result in an OpenAPI schema object that looks like this:

{
    "info": { "title": "Litestar API", "version": "1.0.0" },
    "openapi": "3.0.3",
    "servers": [{ "url": "/" }],
    "paths": {
        "/": {
            "post": {
                "tags": ["ok"],
                "summary": "Route",
                "description": "Requires OK, Returns OK",
                "operationId": "Route",
                "requestBody": {
                    "content": {
                        "text": {
                            "schema": { "type": "string", "title": "Body", "example": "OK" }
                        }
                    },
                    "description": "OK is the only accepted value",
                    "required": false
                },
                "responses": {
                    "201": {
                        "description": "Document created, URL follows",
                        "headers": {}
                    }
                },
                "deprecated": false,
                "x-codeSamples": [
                    {
                        "lang": "Python",
                        "source": "import requests; requests.get('localhost/example')",
                        "label": "Python"
                    },
                    {
                        "lang": "cURL",
                        "source": "curl -XGET localhost/example",
                        "label": "curl"
                    }
                ]
            }
        }
    },
    "components": { "schemas": {} }
}

Attention

OpenAPI Vendor Extension fields need to start with x- and should not be processed with the default field name converter. To work around this, Litestar will honor an alias field provided to the dataclass.field metadata when generating the field name in the schema.