v1.0.0rc2
Pre-release
Pre-release
Added
Annotatedparameter style following modern FastAPI conventions:Annotated[int, Query(ge=1)],Annotated[Item, Body()],Annotated[str, Header()],Annotated[X, Depends(f)]— supported in runtime resolution and OpenAPI generation- Automatic HEAD responses for GET routes on all frameworks (previously 405 on aiohttp, Sanic, Falcon, Tornado). An explicit
@router.head(...)route always takes precedence; auto-HEAD does not appear in the OpenAPI schema debugrouter flag: unhandled 5xx responses include exception details in the body only whendebug=Truepy.typedmarker (PEP 561): the package's type annotations are now visible to users' type checkersregister_docsparameter forDjangoRouteras an explicit alternative to theapp=Trueflag- Cross-framework integration test matrix: one shared application exercised against all 10 router variants (~100 scenarios each), including async endpoints and async/generator dependencies
Changed
- Unhandled 5xx responses no longer leak exception text to clients — a generic message is returned, details go to the log (and to the body only with
debug=True) - Malformed JSON returns 422 with an explicit "Invalid JSON in request body" error (was 400 in 0.7; silently treated as an empty body in rc1)
- Plain
strresults are served astext/plainon every framework instead of being JSON-encoded (0.7) or framework-dependent (rc1);bytesresults default toapplication/octet-stream - Response serialization unified into a single layer shared by all adapters: identical content-type defaults, custom headers preserved on 204/304 everywhere, case-insensitive Content-Type handling
- Request payloads are parsed lazily: body/form/files are only read when the endpoint (or its dependencies) declare matching parameters
- Security schemes are opt-in: schemas no longer include a default BearerAuth;
SecuritySchemeType.OAUTH2now requires explicit dict configuration (raisesValueError, no moreexample.comtoken URL) - Union annotations render as
anyOfin OpenAPI (was a degenerate{"type": "string"}); withopenapi_version="3.1.0"nullability usestype: [..., "null"]instead of the 3.0nullablekeyword - Name collisions get suffixes: distinct models sharing a
__name__produceUser/User2schemas instead of silently reusing the first one; duplicate autogeneratedoperationIds are deduplicated the same way - Async endpoints on sync-only routers (Flask, Falcon sync, Django sync) fail at registration time with a
TypeErrorinstead of a 500 on every request - Tornado 405 responses return the JSON error envelope with an
Allowheader (was tornado's HTML error page) - Framework extras loosened to major-version caps (
aiohttp <4,starlette <1,sanic <26,quart <1,tornado <7);python-multipartis now installed withfastopenapi[starlette] - ReDoc pinned to 2.5.3 (was the floating
redoc@nexttag) MissingRouternow names the missing framework and suggests the extra to install
Fixed
- Generator (
yield) dependencies stay open while the endpoint runs and finish with context-manager semantics — previously the cleanup code afteryieldexecuted before the endpoint was called, so a yielded resource (e.g. a DB session) was already closed when used. Now the code afteryieldruns after the response is built (in reverse creation order, sync and async), and when the pipeline fails the exception is thrown into the dependency —try/except/finallycommit/rollback patterns work as in FastAPI. This includes request cancellation:CancelledErrorreaches the dependency instead of triggering the success path. A dependency that handles the exception suppresses it for dependencies finishing after it, and a failing teardown becomes the exception for the remaining ones, as withcontextlib.ExitStack. An unsuppressed teardown failure on the success path (e.g. a failing commit) turns the response into a 500 instead of a silent 200 — unlike FastAPI, cleanup happens before the response is sent, so the adapter can still report it. Note: this also means framework-native streaming responses must not read from yielded resources lazily Securityscopes are part of the dependency cache key — twoSecurity(dep, scopes=...)declarations with different scopes on one endpoint no longer share a cached result (the second check silently received the first one's scopes)- OpenAPI schema documents parameters declared inside
Depends/Securitydependencies —Query/Header/Cookie/form/body parameters of (nested) dependency functions and class dependencies now appear in the operation, matching what the runtime actually requires;SecurityScopesinjections andHeader(alias="Authorization")stay hidden - Parameters inside dependencies follow HTTP method semantics — a bare Pydantic model in a dependency on GET/HEAD/DELETE resolves from query parameters, as it does on the endpoint itself (previously it always tried the request body)
- Endpoints decorated with several HTTP methods keep each route's own metadata — previously the last decorator's meta (
status_code,response_model,tags, ...) leaked into every method at runtime, andinclude_router()stamped it onto all included routes. Route metadata now travels with the route instead of being re-read from the endpoint function - Failed cleanup of
yielddependencies is logged (fastopenapilogger) instead of being silently swallowed — a broken rollback/close no longer disappears without a trace MissingRouterno longer masks broken import chains — the "X is not installed" hint is raised only when the framework package itself is absent; a broken transitive import (or an internal bug) surfaces its real error messagelist[FileUpload]is documented as an array of binaries — the schema previously showed a single file while the runtime accepted severalSecuritynested insideDependsmarks the operation as protected in the schema; with several registered security schemes each one is listed as an accepted alternative instead of silently picking the first. Scope lists are attached only tooauth2/openIdConnectschemes — other types get an empty array, as the spec requiresresponse_model=str/bytesare documented astext/plain/application/octet-stream, matching what the serializer actually sends (was alwaysapplication/json). Forstr | None/bytes | Noneboth media types are documented:Noneis serialized as a JSONnull- Non-standard status codes (e.g. 299, 499) no longer crash schema generation — they get a generic
Status <code>description; a duplicate explicitoperationIdis reported with a warning instead of silently producing an invalid schema - Route decorators preserve the endpoint's type for type checkers (
(F) -> Finstead ofAny), sopy.typedconsumers keep full signatures of decorated functions - Broken imports in examples and README fixed (
fastopenapi.routers.sync.flask,fastopenapi.routers.tornado) - aiohttp multipart uploads no longer fail with 500 — the JSON body reader drained the stream before the multipart parser could run
- Falcon ASGI urlencoded forms and multipart no longer fail with 500 — async extractors got real async implementations instead of inheriting WSGI-only code
- Falcon multipart with text fields no longer rejected as malformed (
secure_filenameprobing on non-file parts) - Sanic
Cookie(...)parameters no longer fail validation (cookie container yields value lists) - Falcon JSON and form bodies with Content-Type parameters (
application/json; charset=utf-8) are no longer dropped - Multi-value form fields are preserved on Flask and Starlette (
getlist/multi_items); same-name file uploads no longer collapse to the last file - Endpoints receive Pydantic model instances again — an rc1 regression passed
list[dict]and degraded rich field types viamodel_dump()(0.7 behavior restored) - DELETE request bodies are read again — an rc1 regression ignored them (0.7 behavior restored, per RFC 9110 and FastAPI)
list[Model]andModel | Noneannotations work as body parameters without an explicitBody()markerDjangoAsyncRouterwith an explicit OPTIONS route no longer returns 500 (Django'sview_is_asyncignores theoptionshandler)- 204/304 responses keep custom headers on every adapter (rc1 fixed only some); Tornado no longer crashes with an
AssertionErroron 304 - Dependency resolution memory leak fixed: signature caches no longer grow per request; cross-request dependency execution locks removed (restores concurrency in threaded WSGI)
Responseobjects and tuples returned from endpoints withresponse_modelno longer fail validation — explicit responses skip it, as in FastAPI- Nested model
$defsare registered incomponents/schemas(no more broken$refs from models expanded into query parameters) - Mixed-type lists in responses no longer crash serialization
Removed
PaginationParamsschema is no longer injected into every generatedcomponents/schemas- Auto-generated descriptions for
page/limit/sort-style query parameters (the library no longer guesses the semantics of user parameter names)