FastAPI Swagger: Auto-Magic… Until You Need Control


One of FastAPI’s biggest selling points is also one of its biggest traps.

You write Python. You add type hints. You use Pydantic.

And suddenly you have:

  • a full OpenAPI spec
  • a polished Swagger UI
  • zero YAML files

No annotations.
No boilerplate.
Just code.

It feels like magic.

Until the day you need control.


The good part: auto-generated OpenAPI

FastAPI does something genuinely impressive:

@app.get("/users/{id}")
async def get_user(id: int):
    ...

That single function:

  • becomes an HTTP endpoint
  • defines parameter types
  • generates request/response schemas
  • appears instantly in Swagger UI

For most CRUD APIs, this is perfect.

You focus on business logic. FastAPI handles documentation.


Where things start to crack: security

The moment you introduce real authentication, things get interesting.

Imagine a backend that supports:

  • session cookies (primary)
  • basic auth (fallback or internal use)

In OpenAPI terms, this is straightforward.

In Go / YAML-style OpenAPI

You might write something like:

components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
    sessionCookie:
      type: apiKey
      in: cookie
      name: session

security:
  - basicAuth: []
  - sessionCookie: []

Swagger now shows both auth methods in the “Authorize” dialog.

Clear. Explicit. Predictable.


In FastAPI… it depends

In FastAPI, your OpenAPI security model is inferred from dependencies.

def get_current_user(
    credentials: HTTPBasicCredentials = Depends(security)
):
    ...

or

def get_current_user(
    session=Depends(get_session_cookie)
):
    ...

What Swagger shows depends on:

  • which dependency you use
  • whether you used Security(...) or Depends(...)
  • how FastAPI interprets that dependency

And here’s the catch:

If you forget to use Security(...), Swagger may silently assume Basic Auth.

Even if:

  • your app actually uses session cookies
  • Basic Auth is only a fallback
  • production traffic never uses it

No error. No warning. Just misleading documentation.


Why this matters more than it seems

Swagger is not just documentation.

It is:

  • a contract
  • a debugging tool
  • a communication layer with frontend and external teams

If Swagger says:

“Use Basic Auth”

but your app actually expects:

“Session cookie”

You’ve already lost clarity.

Auto-magic stops being helpful when it lies politely.


The moment you outgrow auto-magic

At some point, you’ll want to:

  • define multiple security schemes explicitly
  • control which endpoints require which auth
  • customize Swagger UI behavior
  • hide or expose features based on environment

That’s when FastAPI’s “just infer it” approach starts to feel restrictive.


Your escape hatches

FastAPI does give you ways out — but they are more manual.

1. Override app.openapi

You can fully control the generated spec:

def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema

    schema = get_openapi(
        title=app.title,
        version=app.version,
        routes=app.routes,
    )

    # modify security schemes here

    app.openapi_schema = schema
    return schema

app.openapi = custom_openapi

Powerful. Verbose. Easy to get wrong if you’re not careful.


2. Export and tweak /openapi.json

Sometimes the simplest approach:

  • generate OpenAPI
  • export it
  • edit it manually
  • use it for docs or clients

You lose some automation, but gain precision.


3. Accept that YAML was doing something useful

If you’ve worked with Go or hand-written OpenAPI specs, this feeling is familiar:

“I kind of miss just writing the YAML.”

Not because YAML is fun — but because explicit control scales better in complex systems.


A note on feature-flagged documentation

One underused trick:

You can modify OpenAPI output based on:

  • environment (dev / staging / prod)
  • feature flags
  • CI/CD preview deployments

For example:

  • hide internal endpoints in production docs
  • expose experimental auth methods only in staging
  • document beta APIs selectively

It’s powerful.

It’s also easy to abuse.

Feature flags in documentation deserve the same discipline as feature flags in code.

But that’s a story for another blog.


The takeaway

FastAPI’s automatic Swagger generation is genuinely excellent.

It:

  • lowers the barrier to entry
  • keeps docs close to code
  • works beautifully for simple and medium APIs

But:

The more your API becomes a product, the more you’ll want YAML-level control again.

Especially for:

  • security schemes
  • authentication UX
  • multi-client APIs
  • long-lived contracts

Auto-magic is great.

Just don’t confuse it with authority.


  • Can Pydantic Fix Untyped Python?
  • Async Doesn’t Make Your System Fast — It Makes It Honest
  • REST to Event-Driven: When to Split Your Backend
  • Designing APIs That Stay Honest Under Load