How to Show Additional Description for Enum Field in OpenAPI Document With Litestar
# Introduction
# Litestar Framework
Litestar is a powerful, flexible, highly performant, and opinionated ASGI framework.
The Litestar framework supports Plugins, ships with dependency injection, security primitives, OpenAPI schema generation, MessagePack, middlewares, a great CLI experience, and much more.
# Scalar
Scalar is the modern open-source developer experience platform for your APIs.
It support add custom specification extensions (starting with a x-) through its plugin API.
The official custom extensions supported by Scalar are:
- x-scalar-environments
- x-scalar-active-environment
- x-codeSamples
- x-displayName
- x-tagGroups
- x-scalar-ignore
- x-additionalPropertiesName
- x-scalar-stability
- x-badges
- x-enum-descriptions
- x-enum-varnames
- x-scalar-sdk-installation
# Minimal Example
# app.py
import enum
import pydantic
from litestar import Litestar, get
from litestar.openapi import OpenAPIConfig
from litestar.openapi.plugins import ScalarRenderPlugin
class Weather(enum.IntEnum):
SUNNY = 1
RAINY = 2
CLOUDY = 3
class WeatherPredict(pydantic.BaseModel):
city: str
weather: Weather = pydantic.Field(description="The weather condition")
@get("/city/{name:str}")
async def get_weather(name: str) -> WeatherPredict:
return WeatherPredict(city=name, weather=Weather.SUNNY)
plugins = []
app = Litestar(
route_handlers=[get_weather],
openapi_config=OpenAPIConfig(
title="Litestar Example",
description="Example of Litestar with Scalar OpenAPI docs",
version="0.0.1",
render_plugins=[ScalarRenderPlugin()],
),
plugins=plugins,
)
# Open Scalar OpenAPI Document
$ litestar run
It will display a great CLI interface like below.
Visit http://127.0.0.1:8000/schema/scalar, you will see the enum field rendered like below.
# Support using The x-enum-descriptions Extension
Below is the screenshot of the OpenAPI document rendered by ScalarRenderPlugin with enum field showing additional description using the x-enum-descriptions extension.
The highlighted lines is all you need to add to your code. The EnumSchemaPlugin modifies the enum field schema to add x-enumDescriptions property. It is then used by the x-enum-descriptions extension.
# app.py
import dataclasses
import enum
from typing import Optional
import pydantic
from litestar import Litestar, get
from litestar.openapi import OpenAPIConfig
from litestar.openapi.plugins import ScalarRenderPlugin
from litestar.openapi.spec import Reference, Schema
from litestar.plugins import OpenAPISchemaPlugin
class Weather(enum.IntEnum):
SUNNY = 1
RAINY = 2
CLOUDY = 3
@classmethod
def get_openapi_descriptions(cls):
mapper = {
cls.SUNNY: "Sunny weather",
cls.RAINY: "Rainy weather",
cls.CLOUDY: "Cloudy weather",
}
return [mapper[i] for i in cls]
class WeatherPredict(pydantic.BaseModel):
city: str
weather: Weather = pydantic.Field(description="The weather condition")
@dataclasses.dataclass
class CustomSchema(Schema):
x_enum_descriptions: Optional[list[str]] = None
@property
def _exclude_fields(self) -> set[str]:
return {"x_enum_descriptions"}
@classmethod
def copy_from(cls, schema: Schema):
return CustomSchema(**schema.__dict__)
def to_schema(self) -> dict:
schema = super().to_schema()
schema |= {"x-enumDescriptions": self.x_enum_descriptions}
return schema
class EnumSchemaPlugin(OpenAPISchemaPlugin):
@staticmethod
def is_plugin_supported_type(value):
return False
def is_plugin_supported_field(self, field_definition) -> bool:
return type(field_definition.annotation) is enum.EnumType
def to_openapi_schema(self, field_definition, schema_creator):
schema = schema_creator.for_enum_field(field_definition)
if isinstance(schema, Reference):
if getattr(field_definition.annotation, "get_openapi_descriptions", None):
registered_schema = schema_creator.schema_registry.from_reference(schema)
new_schema = CustomSchema.copy_from(registered_schema.schema)
new_schema.x_enum_descriptions = field_definition.annotation.get_openapi_descriptions()
registered_schema.schema = new_schema
return schema
raise RuntimeError("Never execute this line, it is unreachable code.")
@get("/city/{name:str}")
async def get_weather(name: str) -> WeatherPredict:
return WeatherPredict(city=name, weather=Weather.SUNNY)
plugins = [EnumSchemaPlugin()]
app = Litestar(
route_handlers=[get_weather],
openapi_config=OpenAPIConfig(
title="Litestar Example",
description="Example of Litestar with Scalar OpenAPI docs",
version="0.0.1",
render_plugins=[ScalarRenderPlugin()],
),
plugins=plugins,
)


