Skip to content

OpenAPI schema breaks for Form, Depends, and model request/response schemas #176

@exemplary-citizen

Description

@exemplary-citizen

Summary

Hey, I ran into a few OpenAPI generation issues while migrating a FastAPI-style app to TurboAPI.

The runtime behavior seems fine, but app.openapi() appears to treat every handler signature parameter as a client-
supplied request param/body field. That causes a few problems:

  • Form() defaults are copied into the schema, so json.dumps(app.openapi()) fails
  • Annotated[..., Depends(...)] parameters show up in request bodies
  • model $refs are emitted, but components.schemas stays empty
  • response_model is accepted by the route decorator but not reflected in the 200 response schema

Minimal Repro

from typing import Annotated
import json

from dhi import BaseModel
from turboapi import Depends, Form, Query, TurboAPI

class SearchRequest(BaseModel):
    query: str
    limit: int = 10

class SearchResponse(BaseModel):
    count: int

def get_session():
    return {"session": True}

SessionDep = Annotated[dict, Depends(get_session)]

app = TurboAPI(title="OpenAPI Repro")

@app.post("/login")
def login(username: str = Form(), password: str = Form()):
    return {"username": username}

@app.post("/search", response_model=SearchResponse)
def search(
    session: SessionDep,
    request: SearchRequest,
    include_archived: bool = Query(default=False),
):
    return SearchResponse(count=request.limit)

schema = app.openapi()
json.dumps(schema)

Expected

  • json.dumps(schema) succeeds
  • /login uses application/x-www-form-urlencoded
  • session is omitted from request body/query params
  • SearchRequest and SearchResponse are added to components.schemas
  • /search request body points to SearchRequest
  • /search 200 response points to SearchResponse
  • explicit Query() markers remain query params even on POST routes

Actual

TypeError: Object of type Form is not JSON serializable

Possible Fix

From a quick look, this seems isolated to python/turboapi/openapi.py.

The request handling path appears to already support these patterns, but OpenAPI generation does not mirror that behavior. It treats dependency and marker parameters as plain request inputs. A possible fix is to make OpenAPI generation mirror those same semantics:

  • skip Depends
  • detect marker types like Form, File, Query, Header, and Cookie
  • avoid non-JSON-serializable defaults
  • register model schemas in components.schemas
  • use route.response_model for the 200 response schema

Happy to open a PR for this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions