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.
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, sojson.dumps(app.openapi())failsAnnotated[..., Depends(...)]parameters show up in request bodies$refs are emitted, butcomponents.schemasstays emptyresponse_modelis accepted by the route decorator but not reflected in the200response schemaMinimal Repro
Expected
json.dumps(schema)succeeds/loginusesapplication/x-www-form-urlencodedsessionis omitted from request body/query paramsSearchRequestandSearchResponseare added tocomponents.schemas/searchrequest body points toSearchRequest/search200response points toSearchResponseQuery()markers remain query params even onPOSTroutesActual
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:
DependsForm,File,Query,Header, andCookiecomponents.schemasroute.response_modelfor the200response schemaHappy to open a PR for this.