DEV Community

Yeongseon Choe
Yeongseon Choe

Posted on • Originally published at Medium

FastAPI-Style Swagger UI for Azure Functions in Python (OpenAPI + Pydantic)

I've been building stuff with Azure Functions in Python recently, and I kept running into the same annoyance:

"Where's my Swagger UI?"

FastAPI gives you this beautiful auto-generated Swagger doc with just a few decorators and some pydantic models.

But Azure Functions? Nothing built-in. No docs. No OpenAPI JSON. Just function.json files and… sadness.

So I decided to do something about it.

💡 What I built

I started hacking on a decorator that could hook into the Azure Function's internals — and a few weekends later, I had this:

  • A @openapi decorator that adds summary, description, tags, models, etc.
  • Automatic method and route detection from function.json
  • pydantic support for request and response schemas
  • Swagger UI served at /api/docs
  • JSON docs at /api/openapi.json and /api/openapi.yaml

All without needing to run a separate web server.

✍️ How it works (with an example)

Here's what it looks like in action:

# hello_openapi/function_app.py  import json import azure.functions as func from azure_functions_openapi.decorator import openapi from azure_functions_openapi.openapi import get_openapi_json, get_openapi_yaml from azure_functions_openapi.swagger_ui import render_swagger_ui app = func.FunctionApp() @openapi( summary="Greet user", route="/api/http_trigger", request_model={"name": "string"}, response_model={"message": "string"}, tags=["Example"] ) @app.function_name(name="http_trigger") @app.route(route="/api/http_trigger", auth_level=func.AuthLevel.ANONYMOUS, methods=["POST"]) def main(req: func.HttpRequest) -> func.HttpResponse: try: data = req.get_json() name = data.get("name", "world") return func.HttpResponse( json.dumps({"message": f"Hello, {name}!"}), mimetype="application/json" ) except Exception as e: return func.HttpResponse(f"Error: {str(e)}", status_code=400) @app.function_name(name="openapi_json") @app.route(route="/api/openapi.json", auth_level=func.AuthLevel.ANONYMOUS, methods=["GET"]) def openapi_json(req: func.HttpRequest) -> func.HttpResponse: return get_openapi_json() @app.function_name(name="openapi_yaml") @app.route(route="/api/openapi.yaml", auth_level=func.AuthLevel.ANONYMOUS, methods=["GET"]) def openapi_yaml(req: func.HttpRequest) -> func.HttpResponse: return get_openapi_yaml() @app.function_name(name="swagger_ui") @app.route(route="/api/docs", auth_level=func.AuthLevel.ANONYMOUS, methods=["GET"]) def swagger_ui(req: func.HttpRequest) -> func.HttpResponse: return render_swagger_ui() 
Enter fullscreen mode Exit fullscreen mode

Once you register the Swagger UI route manually, deploying your function lets you access the full documentation at /api/docs.

🚀 Quick Start

pip install azure-functions-openapi 
Enter fullscreen mode Exit fullscreen mode

Then:

  • Decorate your functions with @openapi(...)
  • Define your request/response models with pydantic or dict
  • Register /api/docs, /api/openapi.json, and /api/openapi.yaml routes
  • Deploy and visit /api/docs 🎉

🔗 GitHub

All the code is open-source here:

👉 https://github.com/yeongseon/azure-functions-openapi

If you try it out, I'd love to hear how it works for you.

PRs, issues, suggestions — all welcome 🙌


Originally published on Medium.

Top comments (0)