Architecture¶
This document explains how azure-functions-openapi transforms decorator metadata into OpenAPI output and Swagger UI responses.
High-level design¶
Function handlers + @openapi metadata
|
v
Thread-safe metadata registry (decorator.py)
|
v
Spec compiler (openapi.py)
| |
v v
JSON / YAML strings Swagger UI HTML response (swagger_ui.py)
Core modules¶
decorator.py¶
Responsibilities:
- provide
@openapi(...) - validate and sanitize decorator inputs
- store operation metadata in
_openapi_registry - expose
get_openapi_registry()snapshot accessor
Key behaviors:
- registry writes are protected by
threading.RLock - tags default to
['default']when not provided - invalid route path or operation ID raises
ValueError
openapi.py¶
Responsibilities:
- compile registry into OpenAPI document (
generate_openapi_spec) - serialize to JSON (
get_openapi_json) and YAML (get_openapi_yaml) - support OpenAPI 3.0.0 and 3.1.0 output
Spec generation flow:
- Read registry entries
- Resolve route and method per operation
- Build responses and request body schemas
- Convert Pydantic models into
components.schemas - Merge security schemes (global + per-operation)
- Return final
specdictionary
3.1-specific conversion:
nullable: true->type: ["<type>", "null"]example->examples
utils.py¶
Responsibilities:
- Pydantic v2 schema extraction (
model_to_schema) $refrewriting to#/components/schemas/...- schema collision resolution for repeated model names
- route and operation ID validation helpers
Important details:
- nested
$defs/definitionsare collected recursively - colliding schema names are suffixed (
_2,_3, ...)
swagger_ui.py¶
Responsibilities:
- render Swagger UI HTML via
render_swagger_ui - apply security headers
- sanitize title and URL inputs
Headers added include:
Content-Security-PolicyX-Content-Type-OptionsX-Frame-OptionsReferrer-Policy- cache prevention headers
cli.py¶
Responsibilities:
- parse
azure-functions-openapi generatecommand - output JSON/YAML to stdout or file
- choose OpenAPI version (
3.0or3.1)
Request lifecycle perspective¶
At startup:
- Python imports function modules
@openapiexecutes and registers metadata
At spec request time (/api/openapi.json or /api/openapi.yaml):
- endpoint function calls
get_openapi_json()orget_openapi_yaml() - generator compiles current registry
- endpoint wraps returned string in
func.HttpResponse
At docs request time (/api/docs):
- endpoint calls
render_swagger_ui(openapi_url=...) - HTML + security headers are returned
- browser fetches OpenAPI document from
openapi_url
Design constraints¶
- no function source parsing; metadata is runtime-decorator driven
- no persistence layer; registry exists in process memory
- assumes module import/registration has already happened
Operational considerations¶
- missing imports can lead to empty
paths - inconsistent
@app.routevs@openapi(route=...)leads to doc/runtime mismatch - model schema generation is resilient but invalid model usage raises explicit errors
Extension points¶
- customize spec metadata via generator arguments (
title,version,description) - configure security centrally (
security_schemes) or per operation (security_scheme) - customize UI CSP and behavior via
render_swagger_ui(...)