Skip to content

API Reference

This page documents the public runtime API exposed by azure-functions-openapi.

Import from package root

All symbols below are exported from azure_functions_openapi.__init__, so you can import from azure_functions_openapi directly.

from azure_functions_openapi import (
    OPENAPI_VERSION_3_0,
    OPENAPI_VERSION_3_1,
    generate_openapi_spec,
    get_openapi_json,
    get_openapi_yaml,
    openapi,
    render_swagger_ui,
)

Public API surface

Symbol Kind Purpose
openapi decorator Attach operation metadata to function handlers
generate_openapi_spec function Build OpenAPI dictionary from decorator registry
get_openapi_json function Build OpenAPI and serialize to JSON string
get_openapi_yaml function Build OpenAPI and serialize to YAML string
render_swagger_ui function Return Swagger UI HttpResponse
OPENAPI_VERSION_3_0 constant OpenAPI version string "3.0.0"
OPENAPI_VERSION_3_1 constant OpenAPI version string "3.1.0"

Decorator behavior model

@openapi stores metadata in a thread-safe registry and the spec functions read from that registry to generate output.

@openapi metadata -> internal registry -> generate_openapi_spec -> JSON/YAML endpoint
                                                    -> render_swagger_ui (browser docs)

Note

get_openapi_json() and get_openapi_yaml() return strings, not HttpResponse. Wrap the returned value in func.HttpResponse in your Azure Function route.

Common usage patterns

Minimal endpoint

@app.route(route="ping", methods=["GET"])
@openapi(summary="Ping", description="Health check endpoint")
def ping(req: func.HttpRequest) -> func.HttpResponse:
    return func.HttpResponse("ok", status_code=200)

With Pydantic request and response

class CreateItemRequest(BaseModel):
    name: str


class ItemResponse(BaseModel):
    id: int
    name: str


@app.route(route="items", methods=["POST"])
@openapi(
    summary="Create item",
    method="post",
    route="/api/items",
    request_model=CreateItemRequest,
    response_model=ItemResponse,
    response={201: {"description": "Created"}},
)
def create_item(req: func.HttpRequest) -> func.HttpResponse:
    ...

With raw schema dictionaries

@app.route(route="raw", methods=["POST"])
@openapi(
    summary="Raw schema example",
    method="post",
    request_body={
        "type": "object",
        "properties": {"value": {"type": "string"}},
        "required": ["value"],
    },
    response={
        200: {
            "description": "OK",
            "content": {
                "application/json": {
                    "schema": {
                        "type": "object",
                        "properties": {"accepted": {"type": "boolean"}},
                    }
                }
            },
        }
    },
)
def raw(req: func.HttpRequest) -> func.HttpResponse:
    ...

Expose OpenAPI + Swagger routes

@app.route(route="openapi.json", methods=["GET"])
def openapi_json(req: func.HttpRequest) -> func.HttpResponse:
    return func.HttpResponse(get_openapi_json(title="My API", version="1.0.0"), mimetype="application/json")


@app.route(route="openapi.yaml", methods=["GET"])
def openapi_yaml(req: func.HttpRequest) -> func.HttpResponse:
    return func.HttpResponse(get_openapi_yaml(title="My API", version="1.0.0"), mimetype="application/x-yaml")


@app.route(route="docs", methods=["GET"])
def docs(req: func.HttpRequest) -> func.HttpResponse:
    return render_swagger_ui(title="My API Docs", openapi_url="/api/openapi.json")

mkdocstrings reference

The sections below are generated directly from source docstrings.

openapi

generate_openapi_spec(title='API', version='1.0.0', openapi_version=OPENAPI_VERSION_3_0, description=DEFAULT_OPENAPI_INFO_DESCRIPTION, security_schemes=None)

Compile an OpenAPI specification from the registry.

Parameters:

Name Type Description Default
title str

API title

'API'
version str

API version

'1.0.0'
openapi_version str

OpenAPI specification version ("3.0.0" or "3.1.0")

OPENAPI_VERSION_3_0
description str

Description for the OpenAPI info object

DEFAULT_OPENAPI_INFO_DESCRIPTION
security_schemes dict[str, dict[str, Any]] | None

Security scheme definitions for components.securitySchemes. Example: {"BearerAuth": {"type": "http", "scheme": "bearer"}}

None

Returns:

Type Description
dict[str, Any]

OpenAPI specification dictionary

Source code in src/azure_functions_openapi/openapi.py
def generate_openapi_spec(
    title: str = "API",
    version: str = "1.0.0",
    openapi_version: str = OPENAPI_VERSION_3_0,
    description: str = DEFAULT_OPENAPI_INFO_DESCRIPTION,
    security_schemes: dict[str, dict[str, Any]] | None = None,
) -> dict[str, Any]:
    """
    Compile an OpenAPI specification from the registry.

    Parameters:
        title: API title
        version: API version
        openapi_version: OpenAPI specification version ("3.0.0" or "3.1.0")
        description: Description for the OpenAPI info object
        security_schemes: Security scheme definitions for components.securitySchemes.
            Example: {"BearerAuth": {"type": "http", "scheme": "bearer"}}

    Returns:
        OpenAPI specification dictionary
    """
    if openapi_version not in (OPENAPI_VERSION_3_0, OPENAPI_VERSION_3_1):
        raise OpenAPISpecConfigError(
            f"Unsupported OpenAPI version: {openapi_version}. Supported: "
            f"{OPENAPI_VERSION_3_0}, {OPENAPI_VERSION_3_1}"
        )

    try:
        registry = get_openapi_registry()
        paths: dict[str, dict[str, Any]] = {}
        components: dict[str, Any] = {"schemas": {}}

        for func_name, meta in registry.items():
            try:
                logical_name = meta.get("function_name") or func_name
                # route & method --------------------------------------------------
                path = f"/{(meta.get('route') or logical_name).lstrip('/')}"
                method = (meta.get("method") or "get").lower()

                # responses -------------------------------------------------------
                responses: dict[str, Any] = {}
                for status, detail in meta.get("response", {}).items():
                    resp = dict(detail)
                    resp.setdefault("description", "")
                    responses[str(status)] = resp

                if meta.get("response_model"):
                    try:
                        model_schema = model_to_schema(meta["response_model"], components)
                        target_status = "200"
                        for status_key in responses:
                            if str(status_key).startswith("2"):
                                target_status = str(status_key)
                                break

                        if target_status not in responses:
                            responses[target_status] = {
                                "description": "Successful Response",
                                "content": {"application/json": {"schema": model_schema}},
                            }
                        else:
                            content = responses[target_status].setdefault("content", {})
                            if not isinstance(content, dict):
                                content = {}
                                responses[target_status]["content"] = content

                            json_content = content.setdefault("application/json", {})
                            if not isinstance(json_content, dict):
                                json_content = {}
                                content["application/json"] = json_content

                            json_content.setdefault("schema", model_schema)
                    except Exception as e:
                        logger.warning(
                            f"Failed to generate response schema for {func_name}: {str(e)}"
                        )
                        _ensure_default_response(responses)

                _ensure_default_response(responses)

                # operation object ------------------------------------------------
                op: dict[str, Any] = {
                    "summary": meta.get("summary", ""),
                    "description": meta.get("description", ""),
                    "operationId": meta.get("operation_id") or f"{method}_{logical_name}",
                    "tags": meta.get("tags") or ["default"],
                    "responses": responses,
                }

                # parameters ------------------------------------------------------
                parameters: list[dict[str, Any]] = meta.get("parameters", [])
                if parameters:
                    op["parameters"] = parameters

                # security --------------------------------------------------------
                security: list[dict[str, list[str]]] = meta.get("security", [])
                if security:
                    op["security"] = security

                # requestBody (POST/PUT/PATCH/DELETE) --------------------------
                if method in {"post", "put", "patch", "delete"}:
                    required = meta.get("request_body_required", True)
                    if meta.get("request_body"):
                        op["requestBody"] = {
                            "required": required,
                            "content": {"application/json": {"schema": meta["request_body"]}},
                        }
                    elif meta.get("request_model"):
                        try:
                            op["requestBody"] = {
                                "required": required,
                                "content": {
                                    "application/json": {
                                        "schema": model_to_schema(meta["request_model"], components)
                                    }
                                },
                            }
                        except Exception as e:
                            logger.warning(
                                f"Failed to generate request schema for {func_name}: {str(e)}"
                            )
                            op["requestBody"] = {
                                "required": required,
                                "content": {"application/json": {"schema": {"type": "object"}}},
                            }

                # merge into paths (support multiple methods per route) ----------
                paths.setdefault(path, {})[method] = op

            except (KeyError, TypeError, ValueError):
                logger.exception("Failed to process function %s", func_name)
                # Continue processing other functions
                continue

        spec: dict[str, Any] = {
            "openapi": openapi_version,
            "info": {
                "title": title,
                "version": version,
                "description": description,
            },
            "paths": paths,
        }

        if openapi_version == OPENAPI_VERSION_3_1:
            spec["info"]["summary"] = title

        # Merge security schemes: explicit param + per-operation schemes from registry.
        # Raises OpenAPISpecConfigError on collision (same name, different definition).
        all_security_schemes: dict[str, dict[str, Any]] = {}
        if security_schemes:
            all_security_schemes.update(security_schemes)
        for _fn, meta in registry.items():
            scheme = meta.get("security_scheme")
            if isinstance(scheme, dict):
                for name, definition in scheme.items():
                    if name in all_security_schemes and all_security_schemes[name] != definition:
                        raise OpenAPISpecConfigError(
                            f"Conflicting security scheme definition for '{name}': "
                            f"existing={all_security_schemes[name]!r}, "
                            f"new={definition!r}"
                        )
                    all_security_schemes[name] = definition

        if all_security_schemes:
            components["securitySchemes"] = all_security_schemes

        if components.get("schemas"):
            if openapi_version == OPENAPI_VERSION_3_1:
                components["schemas"] = _convert_schemas_to_3_1(components["schemas"])

        if components.get("schemas") or components.get("securitySchemes"):
            spec["components"] = components

        spec = _normalize_spec_output(spec)

        logger.info(
            f"Generated OpenAPI {openapi_version} spec with {len(paths)} paths "
            f"for {len(registry)} functions"
        )
        return spec

    except OpenAPISpecConfigError:
        raise
    except Exception as e:
        logger.error(f"Failed to generate OpenAPI specification: {str(e)}")
        raise RuntimeError("Failed to generate OpenAPI specification") from e

get_openapi_json(title='API', version='1.0.0', openapi_version=OPENAPI_VERSION_3_0, description=DEFAULT_OPENAPI_INFO_DESCRIPTION, security_schemes=None)

Return the spec as pretty-printed JSON (UTF-8).

Parameters:

Name Type Description Default
title str

API title

'API'
version str

API version

'1.0.0'
openapi_version str

OpenAPI specification version ("3.0.0" or "3.1.0")

OPENAPI_VERSION_3_0
description str

Description for the OpenAPI info object

DEFAULT_OPENAPI_INFO_DESCRIPTION
security_schemes dict[str, dict[str, Any]] | None

Security scheme definitions for components.securitySchemes.

None

Returns:

Type Description
str

OpenAPI spec in JSON format.

Source code in src/azure_functions_openapi/openapi.py
def get_openapi_json(
    title: str = "API",
    version: str = "1.0.0",
    openapi_version: str = OPENAPI_VERSION_3_0,
    description: str = DEFAULT_OPENAPI_INFO_DESCRIPTION,
    security_schemes: dict[str, dict[str, Any]] | None = None,
) -> str:
    """Return the spec as pretty-printed JSON (UTF-8).

    Parameters:
        title: API title
        version: API version
        openapi_version: OpenAPI specification version ("3.0.0" or "3.1.0")
        description: Description for the OpenAPI info object
        security_schemes: Security scheme definitions for components.securitySchemes.

    Returns:
        OpenAPI spec in JSON format.
    """
    try:
        spec = generate_openapi_spec(
            title, version, openapi_version,
            description=description,
            security_schemes=security_schemes,
        )
        return json.dumps(spec, indent=2, ensure_ascii=False)
    except OpenAPISpecConfigError:
        raise
    except Exception as e:
        logger.error(f"Failed to generate OpenAPI JSON: {str(e)}")
        raise RuntimeError("Failed to generate OpenAPI JSON") from e

get_openapi_yaml(title='API', version='1.0.0', openapi_version=OPENAPI_VERSION_3_0, description=DEFAULT_OPENAPI_INFO_DESCRIPTION, security_schemes=None)

Return the spec as YAML.

Parameters:

Name Type Description Default
title str

API title

'API'
version str

API version

'1.0.0'
openapi_version str

OpenAPI specification version ("3.0.0" or "3.1.0")

OPENAPI_VERSION_3_0
description str

Description for the OpenAPI info object

DEFAULT_OPENAPI_INFO_DESCRIPTION
security_schemes dict[str, dict[str, Any]] | None

Security scheme definitions for components.securitySchemes.

None

Returns:

Type Description
str

OpenAPI spec in YAML format.

Source code in src/azure_functions_openapi/openapi.py
def get_openapi_yaml(
    title: str = "API",
    version: str = "1.0.0",
    openapi_version: str = OPENAPI_VERSION_3_0,
    description: str = DEFAULT_OPENAPI_INFO_DESCRIPTION,
    security_schemes: dict[str, dict[str, Any]] | None = None,
) -> str:
    """Return the spec as YAML.

    Parameters:
        title: API title
        version: API version
        openapi_version: OpenAPI specification version ("3.0.0" or "3.1.0")
        description: Description for the OpenAPI info object
        security_schemes: Security scheme definitions for components.securitySchemes.

    Returns:
        OpenAPI spec in YAML format.
    """
    try:
        spec = generate_openapi_spec(
            title, version, openapi_version,
            description=description,
            security_schemes=security_schemes,
        )
        return yaml.safe_dump(spec, sort_keys=False, allow_unicode=True)
    except OpenAPISpecConfigError:
        raise
    except Exception as e:
        logger.error(f"Failed to generate OpenAPI YAML: {str(e)}")
        raise RuntimeError("Failed to generate OpenAPI YAML") from e

generate_openapi_spec

Compile an OpenAPI specification from the registry.

Parameters:

Name Type Description Default
title str

API title

'API'
version str

API version

'1.0.0'
openapi_version str

OpenAPI specification version ("3.0.0" or "3.1.0")

OPENAPI_VERSION_3_0
description str

Description for the OpenAPI info object

DEFAULT_OPENAPI_INFO_DESCRIPTION
security_schemes dict[str, dict[str, Any]] | None

Security scheme definitions for components.securitySchemes. Example: {"BearerAuth": {"type": "http", "scheme": "bearer"}}

None

Returns:

Type Description
dict[str, Any]

OpenAPI specification dictionary

Source code in src/azure_functions_openapi/openapi.py
def generate_openapi_spec(
    title: str = "API",
    version: str = "1.0.0",
    openapi_version: str = OPENAPI_VERSION_3_0,
    description: str = DEFAULT_OPENAPI_INFO_DESCRIPTION,
    security_schemes: dict[str, dict[str, Any]] | None = None,
) -> dict[str, Any]:
    """
    Compile an OpenAPI specification from the registry.

    Parameters:
        title: API title
        version: API version
        openapi_version: OpenAPI specification version ("3.0.0" or "3.1.0")
        description: Description for the OpenAPI info object
        security_schemes: Security scheme definitions for components.securitySchemes.
            Example: {"BearerAuth": {"type": "http", "scheme": "bearer"}}

    Returns:
        OpenAPI specification dictionary
    """
    if openapi_version not in (OPENAPI_VERSION_3_0, OPENAPI_VERSION_3_1):
        raise OpenAPISpecConfigError(
            f"Unsupported OpenAPI version: {openapi_version}. Supported: "
            f"{OPENAPI_VERSION_3_0}, {OPENAPI_VERSION_3_1}"
        )

    try:
        registry = get_openapi_registry()
        paths: dict[str, dict[str, Any]] = {}
        components: dict[str, Any] = {"schemas": {}}

        for func_name, meta in registry.items():
            try:
                logical_name = meta.get("function_name") or func_name
                # route & method --------------------------------------------------
                path = f"/{(meta.get('route') or logical_name).lstrip('/')}"
                method = (meta.get("method") or "get").lower()

                # responses -------------------------------------------------------
                responses: dict[str, Any] = {}
                for status, detail in meta.get("response", {}).items():
                    resp = dict(detail)
                    resp.setdefault("description", "")
                    responses[str(status)] = resp

                if meta.get("response_model"):
                    try:
                        model_schema = model_to_schema(meta["response_model"], components)
                        target_status = "200"
                        for status_key in responses:
                            if str(status_key).startswith("2"):
                                target_status = str(status_key)
                                break

                        if target_status not in responses:
                            responses[target_status] = {
                                "description": "Successful Response",
                                "content": {"application/json": {"schema": model_schema}},
                            }
                        else:
                            content = responses[target_status].setdefault("content", {})
                            if not isinstance(content, dict):
                                content = {}
                                responses[target_status]["content"] = content

                            json_content = content.setdefault("application/json", {})
                            if not isinstance(json_content, dict):
                                json_content = {}
                                content["application/json"] = json_content

                            json_content.setdefault("schema", model_schema)
                    except Exception as e:
                        logger.warning(
                            f"Failed to generate response schema for {func_name}: {str(e)}"
                        )
                        _ensure_default_response(responses)

                _ensure_default_response(responses)

                # operation object ------------------------------------------------
                op: dict[str, Any] = {
                    "summary": meta.get("summary", ""),
                    "description": meta.get("description", ""),
                    "operationId": meta.get("operation_id") or f"{method}_{logical_name}",
                    "tags": meta.get("tags") or ["default"],
                    "responses": responses,
                }

                # parameters ------------------------------------------------------
                parameters: list[dict[str, Any]] = meta.get("parameters", [])
                if parameters:
                    op["parameters"] = parameters

                # security --------------------------------------------------------
                security: list[dict[str, list[str]]] = meta.get("security", [])
                if security:
                    op["security"] = security

                # requestBody (POST/PUT/PATCH/DELETE) --------------------------
                if method in {"post", "put", "patch", "delete"}:
                    required = meta.get("request_body_required", True)
                    if meta.get("request_body"):
                        op["requestBody"] = {
                            "required": required,
                            "content": {"application/json": {"schema": meta["request_body"]}},
                        }
                    elif meta.get("request_model"):
                        try:
                            op["requestBody"] = {
                                "required": required,
                                "content": {
                                    "application/json": {
                                        "schema": model_to_schema(meta["request_model"], components)
                                    }
                                },
                            }
                        except Exception as e:
                            logger.warning(
                                f"Failed to generate request schema for {func_name}: {str(e)}"
                            )
                            op["requestBody"] = {
                                "required": required,
                                "content": {"application/json": {"schema": {"type": "object"}}},
                            }

                # merge into paths (support multiple methods per route) ----------
                paths.setdefault(path, {})[method] = op

            except (KeyError, TypeError, ValueError):
                logger.exception("Failed to process function %s", func_name)
                # Continue processing other functions
                continue

        spec: dict[str, Any] = {
            "openapi": openapi_version,
            "info": {
                "title": title,
                "version": version,
                "description": description,
            },
            "paths": paths,
        }

        if openapi_version == OPENAPI_VERSION_3_1:
            spec["info"]["summary"] = title

        # Merge security schemes: explicit param + per-operation schemes from registry.
        # Raises OpenAPISpecConfigError on collision (same name, different definition).
        all_security_schemes: dict[str, dict[str, Any]] = {}
        if security_schemes:
            all_security_schemes.update(security_schemes)
        for _fn, meta in registry.items():
            scheme = meta.get("security_scheme")
            if isinstance(scheme, dict):
                for name, definition in scheme.items():
                    if name in all_security_schemes and all_security_schemes[name] != definition:
                        raise OpenAPISpecConfigError(
                            f"Conflicting security scheme definition for '{name}': "
                            f"existing={all_security_schemes[name]!r}, "
                            f"new={definition!r}"
                        )
                    all_security_schemes[name] = definition

        if all_security_schemes:
            components["securitySchemes"] = all_security_schemes

        if components.get("schemas"):
            if openapi_version == OPENAPI_VERSION_3_1:
                components["schemas"] = _convert_schemas_to_3_1(components["schemas"])

        if components.get("schemas") or components.get("securitySchemes"):
            spec["components"] = components

        spec = _normalize_spec_output(spec)

        logger.info(
            f"Generated OpenAPI {openapi_version} spec with {len(paths)} paths "
            f"for {len(registry)} functions"
        )
        return spec

    except OpenAPISpecConfigError:
        raise
    except Exception as e:
        logger.error(f"Failed to generate OpenAPI specification: {str(e)}")
        raise RuntimeError("Failed to generate OpenAPI specification") from e

get_openapi_json

Return the spec as pretty-printed JSON (UTF-8).

Parameters:

Name Type Description Default
title str

API title

'API'
version str

API version

'1.0.0'
openapi_version str

OpenAPI specification version ("3.0.0" or "3.1.0")

OPENAPI_VERSION_3_0
description str

Description for the OpenAPI info object

DEFAULT_OPENAPI_INFO_DESCRIPTION
security_schemes dict[str, dict[str, Any]] | None

Security scheme definitions for components.securitySchemes.

None

Returns:

Type Description
str

OpenAPI spec in JSON format.

Source code in src/azure_functions_openapi/openapi.py
def get_openapi_json(
    title: str = "API",
    version: str = "1.0.0",
    openapi_version: str = OPENAPI_VERSION_3_0,
    description: str = DEFAULT_OPENAPI_INFO_DESCRIPTION,
    security_schemes: dict[str, dict[str, Any]] | None = None,
) -> str:
    """Return the spec as pretty-printed JSON (UTF-8).

    Parameters:
        title: API title
        version: API version
        openapi_version: OpenAPI specification version ("3.0.0" or "3.1.0")
        description: Description for the OpenAPI info object
        security_schemes: Security scheme definitions for components.securitySchemes.

    Returns:
        OpenAPI spec in JSON format.
    """
    try:
        spec = generate_openapi_spec(
            title, version, openapi_version,
            description=description,
            security_schemes=security_schemes,
        )
        return json.dumps(spec, indent=2, ensure_ascii=False)
    except OpenAPISpecConfigError:
        raise
    except Exception as e:
        logger.error(f"Failed to generate OpenAPI JSON: {str(e)}")
        raise RuntimeError("Failed to generate OpenAPI JSON") from e

get_openapi_yaml

Return the spec as YAML.

Parameters:

Name Type Description Default
title str

API title

'API'
version str

API version

'1.0.0'
openapi_version str

OpenAPI specification version ("3.0.0" or "3.1.0")

OPENAPI_VERSION_3_0
description str

Description for the OpenAPI info object

DEFAULT_OPENAPI_INFO_DESCRIPTION
security_schemes dict[str, dict[str, Any]] | None

Security scheme definitions for components.securitySchemes.

None

Returns:

Type Description
str

OpenAPI spec in YAML format.

Source code in src/azure_functions_openapi/openapi.py
def get_openapi_yaml(
    title: str = "API",
    version: str = "1.0.0",
    openapi_version: str = OPENAPI_VERSION_3_0,
    description: str = DEFAULT_OPENAPI_INFO_DESCRIPTION,
    security_schemes: dict[str, dict[str, Any]] | None = None,
) -> str:
    """Return the spec as YAML.

    Parameters:
        title: API title
        version: API version
        openapi_version: OpenAPI specification version ("3.0.0" or "3.1.0")
        description: Description for the OpenAPI info object
        security_schemes: Security scheme definitions for components.securitySchemes.

    Returns:
        OpenAPI spec in YAML format.
    """
    try:
        spec = generate_openapi_spec(
            title, version, openapi_version,
            description=description,
            security_schemes=security_schemes,
        )
        return yaml.safe_dump(spec, sort_keys=False, allow_unicode=True)
    except OpenAPISpecConfigError:
        raise
    except Exception as e:
        logger.error(f"Failed to generate OpenAPI YAML: {str(e)}")
        raise RuntimeError("Failed to generate OpenAPI YAML") from e

render_swagger_ui

Render Swagger UI with enhanced security headers and CSP protection.

Parameters:

Name Type Description Default
title str

Page title for the Swagger UI

'API Documentation'
openapi_url str

URL to the OpenAPI specification

'/api/openapi.json'
custom_csp str | None

Custom Content Security Policy (optional)

None
enable_client_logging bool

Whether to enable browser-side response logging

False

Returns:

Type Description
HttpResponse

HttpResponse with Swagger UI HTML and security headers

Source code in src/azure_functions_openapi/swagger_ui.py
def render_swagger_ui(
    title: str = "API Documentation",
    openapi_url: str = "/api/openapi.json",
    custom_csp: str | None = None,
    enable_client_logging: bool = False,
) -> HttpResponse:
    """
    Render Swagger UI with enhanced security headers and CSP protection.

    Parameters:
        title: Page title for the Swagger UI
        openapi_url: URL to the OpenAPI specification
        custom_csp: Custom Content Security Policy (optional)
        enable_client_logging: Whether to enable browser-side response logging

    Returns:
        HttpResponse with Swagger UI HTML and security headers
    """
    nonce = secrets.token_urlsafe(16)

    # Enhanced CSP policy for better security
    default_csp = (
        "default-src 'self'; "
        f"script-src 'self' 'nonce-{nonce}' https://cdn.jsdelivr.net; "
        "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; "
        "img-src 'self' data: https:; "
        "font-src 'self' https://cdn.jsdelivr.net; "
        "connect-src 'self'; "
        "frame-ancestors 'none'; "
        "base-uri 'self'; "
        "form-action 'self'"
    )

    csp_policy = custom_csp or default_csp

    # Validate and sanitize inputs
    sanitized_title = _sanitize_html_content(title)
    sanitized_url = _sanitize_url(openapi_url)
    response_interceptor = """
            responseInterceptor: function(response) {
              return response;
            }
    """
    if enable_client_logging:
        response_interceptor = """
            responseInterceptor: function(response) {
              console.log('API Response:', response.status, response.url);
              return response;
            }
    """

    html_content = f"""
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta http-equiv="Content-Security-Policy" content="{csp_policy}">
        <meta http-equiv="X-Content-Type-Options" content="nosniff">
        <meta http-equiv="X-Frame-Options" content="DENY">
        <meta http-equiv="X-XSS-Protection" content="1; mode=block">
        <meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin">
        <title>{sanitized_title}</title>
        <link rel="stylesheet" 
              type="text/css" 
              href="https://cdn.jsdelivr.net/npm/swagger-ui-dist/swagger-ui.css" />
      </head>
      <body>
        <div id="swagger-ui"></div>
        <script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist/swagger-ui-bundle.js"></script>
        <script nonce="{nonce}">
          // Enhanced security configuration
          const ui = SwaggerUIBundle({{
            url: '{sanitized_url}',
            dom_id: '#swagger-ui',
            presets: [SwaggerUIBundle.presets.apis],
            layout: 'BaseLayout',
            validatorUrl: null,  // Disable external validator for security
            tryItOutEnabled: true,
            supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
            requestInterceptor: function(request) {{
              // Add security headers to requests
              request.headers['X-Requested-With'] = 'XMLHttpRequest';
              return request;
            }},
            {response_interceptor}
          }});
        </script>
      </body>
    </html>
    """

    # Create response with security headers
    response = HttpResponse(html_content, mimetype="text/html")

    # Add additional security headers
    headers = {
        "Content-Security-Policy": csp_policy,
        "X-Content-Type-Options": "nosniff",
        "X-Frame-Options": "DENY",
        "X-XSS-Protection": "1; mode=block",
        "Referrer-Policy": "strict-origin-when-cross-origin",
        "Strict-Transport-Security": "max-age=31536000; includeSubDomains",
        "Cache-Control": "no-cache, no-store, must-revalidate",
        "Pragma": "no-cache",
        "Expires": "0",
    }

    for header, value in headers.items():
        response.headers[header] = value

    logger.info(f"Swagger UI rendered with enhanced security headers for URL: {sanitized_url}")
    return response

While not part of the top-level runtime import list for app code, these internals are useful when debugging:

  • Registry accessor: azure_functions_openapi.decorator.get_openapi_registry
  • Route sanitizer: azure_functions_openapi.utils.validate_route_path
  • Operation ID sanitizer: azure_functions_openapi.utils.sanitize_operation_id

Version constants

Use these constants for explicit version selection:

from azure_functions_openapi import OPENAPI_VERSION_3_0, OPENAPI_VERSION_3_1

spec_30 = get_openapi_json(openapi_version=OPENAPI_VERSION_3_0)
spec_31 = get_openapi_json(openapi_version=OPENAPI_VERSION_3_1)

Tip

Prefer constants over hardcoded strings to avoid typos and keep version intent explicit in code review.