Skip to content

OpenAPI Customisation

FasterAPI generates an OpenAPI 3.x schema automatically. This page covers how to conditionally show it, extend it, and override generated metadata.

Disable docs in production

import os
from FasterAPI import Faster

is_prod = os.environ.get("ENV") == "production"

app = Faster(
    docs_url=None if is_prod else "/docs",
    redoc_url=None if is_prod else "/redoc",
    openapi_url=None if is_prod else "/openapi.json",
)

Change the docs URLs

app = Faster(
    docs_url="/api/docs",
    redoc_url="/api/redoc",
    openapi_url="/api/openapi.json",
)

Add tags metadata

Provide descriptions and external documentation links for each tag:

from FasterAPI import Faster

tags_metadata = [
    {
        "name": "items",
        "description": "Operations on inventory items.",
    },
    {
        "name": "users",
        "description": "User management. See the [docs](https://example.com).",
        "externalDocs": {
            "description": "External docs",
            "url": "https://example.com/users",
        },
    },
]

app = Faster(
    title="Inventory API",
    description="Full inventory management system.",
    version="1.0.0",
)

Route-level OpenAPI fields

@app.get(
    "/items/{item_id}",
    summary="Get item by ID",
    tags=["items"],
    deprecated=False,
    status_code=200,
)
async def get_item(item_id: int):
    """Retrieve a single inventory item.

    Returns 404 if the item does not exist.
    """
    ...

The docstring becomes the route description in Swagger UI.

Extending the generated schema

The schema is generated by FasterAPI.openapi.generator.generate_openapi. To add custom fields (e.g. servers, x-internal), intercept the /openapi.json endpoint:

import copy
from FasterAPI import Faster, JSONResponse
from FasterAPI.openapi.generator import generate_openapi

app = Faster(openapi_url=None)  # disable default endpoint


def _custom_openapi():
    schema = generate_openapi(app, title=app.title, version=app.version)
    schema = copy.deepcopy(schema)
    schema["servers"] = [
        {"url": "https://api.example.com", "description": "Production"},
        {"url": "http://localhost:8000", "description": "Development"},
    ]
    schema["info"]["x-logo"] = {"url": "https://example.com/logo.png"}
    return schema


@app.get("/openapi.json", include_in_schema=False)
async def openapi_schema():
    return JSONResponse(_custom_openapi())

Adding security schemes

_openapi_cache = None


def get_openapi_schema():
    global _openapi_cache
    if _openapi_cache is None:
        schema = generate_openapi(app, title=app.title, version=app.version)
        schema.setdefault("components", {}).setdefault("securitySchemes", {}).update({
            "BearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT",
            }
        })
        schema["security"] = [{"BearerAuth": []}]
        _openapi_cache = schema
    return _openapi_cache

Hiding internal routes

Set include_in_schema=False (currently achieved by using the openapi tag and filtering, or simply by not declaring the route through the public decorator):

# Internal health probe — not shown in API docs
async def _internal_health():
    return {"ok": True}

app._add_route(
    "GET",
    "/_health",
    _internal_health,
    tags=["internal"],
    summary="",
    response_model=None,
    status_code=200,
    deprecated=False,
)

Next steps