Changelog¶
All notable changes to FastOpenAPI are documented in this file.
FastOpenAPI follows the Keep a Changelog format.
[1.0.0rc2] - 2026-07-13¶
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)
[1.0.0rc1] - 2026-03-11¶
Added¶
- Multi-body parameter support following FastAPI semantics: multiple body parameters are automatically embedded by name,
Body(embed=True)supported for single parameters
Fixed¶
- GET + Pydantic model now correctly reads query params instead of body at runtime
- Aiohttp multipart reader no longer consumed twice — form fields and files parsed in a single pass with caching
- Falcon async router binary/text responses now use proper content-type branching (was forcing
response.mediafor all) - Async DI resolver no longer holds
threading.Lockacrossawait— removed unnecessary lock since async is single-threaded per event loop - Tornado cookies now extracted as strings instead of
Morselobjects - 204 responses now preserve custom headers from tuple returns and
Responseobjects - OpenAPI schema invalidated after
add_route/include_router— no more stale schema if accessed before all routes are registered - JSON array in request body returns 422 instead of 500 when endpoint expects a single Pydantic model
- DI cache reliability improvements
- Error hierarchy backward compatibility fixes
[1.0.0b1] - 2026-02-09¶
Added¶
- Dependency Injection system with
DependsandSecurityfor automatic dependency resolution - Request-scoped caching (same dependency called multiple times returns cached result)
- Circular dependency detection
SecurityScopesinjection for OAuth2 scope validation- Generator/yield dependencies with proper cleanup (sync and async)
- FastAPI-style parameter classes:
Query,Path,Header,Cookie,Body,Form,File - Full Pydantic v2 validation:
gt,ge,lt,le,min_length,max_length,pattern,multiple_of,strict, etc. - Parameter metadata:
description,title,example,examples,deprecated - Alias support for headers and query parameters
- Django support with
DjangoRouter(sync) andDjangoAsyncRouter(async), includingurlsproperty for Django URL patterns - Falcon async support with
FalconAsyncRouter(in addition to existing syncFalconRouter) FileUploadclass for framework-agnostic file handling with.read()and.aread()methods- Form data and file upload extraction for all frameworks
RequestDataunified container for request data across all frameworksResponseclass for custom responses with headers and status codes- Response model validation via
TypeAdapterwith thread-safe caching - Standardized error hierarchy:
APIError,BadRequestError,ValidationError(422),AuthenticationError,AuthorizationError,ResourceNotFoundError,ResourceConflictError,InternalServerError,ServiceUnavailableError,DependencyError,CircularDependencyError,SecurityError APIError.from_exception()for converting any exception to standardized JSON formatEXCEPTION_MAPPERon routers for framework-specific exception conversion (e.g., Django'sPermissionDenied→AuthorizationError)- Built-in OpenAPI security schemes: Bearer JWT, API Key (header/query), Basic Auth, OAuth2
- Custom security schemes via
security_schemeparameter (acceptsSecuritySchemeTypeenum or raw dict) - Security scheme merging in
include_router() SecuritySchemeTypeexported fromfastopenapifor public use- Documentation completely rewritten with guides, API reference, framework-specific pages, and examples
Changed¶
- Complete architecture refactor from monolithic
base_router.pyto composition-based modular design: core/—BaseRouter, parameter classes, dependency resolver, types, constantsresolution/—ParameterResolver(extracted fromBaseRouter.resolve_endpoint_params())response/—ResponseBuilder(extracted fromBaseRouter._serialize_response())openapi/—OpenAPIGenerator,SchemaBuilder, UI renderers (extracted fromBaseRouter.generate_openapi())errors/— error hierarchy (extracted fromerror_handler.py)routers/—BaseAdapter+ per-framework packages with separate extractors- All framework routers now inherit from
BaseAdapterinstead ofBaseRouter - Each framework router split into separate router and extractor modules
- Route metadata stored in
RouteInfoclass (was tuple) - Validation errors now return HTTP 422 (was 400)
- OpenAPI
summaryresolved from route metadata or formatted endpoint name;descriptionfrom metadata or docstring - Improved import errors with
MissingRouterraisingImportErrorwhen framework is not installed djangoadded as optional dependency extra
Deprecated¶
- Importing from
fastopenapi.error_handlermodule (usefrom fastopenapi.errors import ...instead)
Removed¶
BaseRouter.generate_openapi()method (userouter.openapiproperty)BaseRouter.resolve_endpoint_params()andBaseRouter._serialize_response()internal methods- Multi-language documentation (single English version retained)
[0.7.0] - 2025-04-27¶
Changed¶
- Replaced
json.dumps/json.loadswith pydantic_coreto_json/from_json _serialize_response: model list mapping now handled by Pydantic instead of manual recursion
Fixed¶
- Issue with parsing repeated query parameters in URL.
Removed¶
- The
use_aliasesfromBaseRouterand reverted changes from 0.6.0.
[0.6.0] – 2025‑04‑16¶
Added¶
- The
use_aliasesparameter was added to theBaseRouterconstructor. Default isTrue. To preserve the previous behavior (without using aliases from Pydantic), setuse_aliases=False.
Changed¶
- The
_serialize_response methodis now an instance method (was a@staticmethod) — to supportuse_aliases. - The
_get_model_schemamethod was temporarily changed from a@classmethodto a regular method — for consistent behavior withuse_aliases.
Deprecated¶
use_aliasesis deprecated and will be removed in version 0.7.0.
[0.5.0] - 2025-04-13¶
Added¶
- AioHttpRouter for integration with the AIOHTTP framework (async support for AIOHTTP).
- Class-level cache for model schemas to improve performance (avoids regenerating JSON Schema for the same Pydantic model repeatedly).
response_errorsparameter for route decorators to document error responses in OpenAPI.error_handlermodule for standard error responses (provides exceptions likeBadRequestError,ResourceNotFoundError, etc., as described in documentation).- Support for using basic Python types (
int,float,bool,str) asresponse_model(for simple responses).
[0.4.0] - 2025-03-20¶
Added¶
- ReDoc UI support. A ReDoc documentation interface is now served at the default URL (e.g.,
/redoc). - TornadoRouter for integration with the Tornado framework.
Changed¶
- Revised and updated all tests to improve coverage and reliability.
Fixed¶
- Status code for internal error responses: changed from 422 to 500 for unhandled exceptions, providing a more appropriate HTTP status for server errors.
Removed¶
- Removed the
add_docs_routeandadd_openapi_routemethods fromBaseRouter. Documentation routes are now added by default, so these manual methods are no longer needed.
[0.3.1] - 2025-03-15¶
Fixed¶
- Fixed import issue for routers when a framework is not installed. (Guarded against
ModuleNotFoundErrorif an extra was not installed and its router was imported.)
[0.3.0] - 2025-03-15¶
Added¶
- QuartRouter for integration with the Quart framework (async Flask-like framework).
- Initial documentation (introduction and basic usage examples) added to repository.
Changed¶
- Import syntax for routers simplified: you can now do
from fastopenapi.routers import YourRouter(e.g.,FlaskRouter) instead of deeper module paths.
Fixed¶
- Fixed retrieving parameters for BaseModel arguments in GET routes. Query parameters based on Pydantic models now work correctly.
[0.2.1] - 2025-03-12¶
Fixed¶
- Fixed an issue in internal response serialization:
_serialize_responsenow correctly handlesBaseModelinstances by converting them to dict before JSON encoding (preventing a TypeError). - Resolved a bug causing
DataLoaderto crash when processing empty datasets. (This appears to be an internal utility, possibly used for schema generation.) - Added more tests to cover these scenarios.
- Added this
CHANGELOG.mdfile to track changes.
[0.2.0] - 2025-03-11¶
Added¶
- Implemented
resolve_endpoint_paramsinBaseRouterto systematically resolve function parameters (path, query, body) and integrate with Pydantic validation. - Added
prefixparameter to theinclude_routermethod, allowing grouping routes under a path. - Implemented
status_codesupport for responses in route decorators (could specify default status code for each endpoint).
Changed¶
- Refactored all router implementations for consistency and to reduce code duplication.
Removed¶
- Removed the
register_routesmethod from Starlette integration (no longer needed after refactor).
[0.1.0] - 2025-03-01¶
Added¶
- Initial release of FastOpenAPI.
- Core functionality implemented:
- Base classes and structure for routers.
- Router support for Falcon, Flask, Sanic, Starlette.
- OpenAPI schema generation leveraging Pydantic v2.
- Basic validation for query and body parameters.
- Included basic documentation in README and a few examples.
- Added initial test suite covering basic route registration and schema generation.