Skip to content

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

  • Annotated parameter 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
  • debug router flag: unhandled 5xx responses include exception details in the body only when debug=True
  • py.typed marker (PEP 561): the package's type annotations are now visible to users' type checkers
  • register_docs parameter for DjangoRouter as an explicit alternative to the app=True flag
  • 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 str results are served as text/plain on every framework instead of being JSON-encoded (0.7) or framework-dependent (rc1); bytes results default to application/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.OAUTH2 now requires explicit dict configuration (raises ValueError, no more example.com token URL)
  • Union annotations render as anyOf in OpenAPI (was a degenerate {"type": "string"}); with openapi_version="3.1.0" nullability uses type: [..., "null"] instead of the 3.0 nullable keyword
  • Name collisions get suffixes: distinct models sharing a __name__ produce User/User2 schemas instead of silently reusing the first one; duplicate autogenerated operationIds are deduplicated the same way
  • Async endpoints on sync-only routers (Flask, Falcon sync, Django sync) fail at registration time with a TypeError instead of a 500 on every request
  • Tornado 405 responses return the JSON error envelope with an Allow header (was tornado's HTML error page)
  • Framework extras loosened to major-version caps (aiohttp <4, starlette <1, sanic <26, quart <1, tornado <7); python-multipart is now installed with fastopenapi[starlette]
  • ReDoc pinned to 2.5.3 (was the floating redoc@next tag)
  • MissingRouter now 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 after yield executed before the endpoint was called, so a yielded resource (e.g. a DB session) was already closed when used. Now the code after yield runs 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/finally commit/rollback patterns work as in FastAPI. This includes request cancellation: CancelledError reaches 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 with contextlib.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
  • Security scopes are part of the dependency cache key — two Security(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/Security dependenciesQuery/Header/Cookie/form/body parameters of (nested) dependency functions and class dependencies now appear in the operation, matching what the runtime actually requires; SecurityScopes injections and Header(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, and include_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 yield dependencies is logged (fastopenapi logger) instead of being silently swallowed — a broken rollback/close no longer disappears without a trace
  • MissingRouter no 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 message
  • list[FileUpload] is documented as an array of binaries — the schema previously showed a single file while the runtime accepted several
  • Security nested inside Depends marks 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 to oauth2/openIdConnect schemes — other types get an empty array, as the spec requires
  • response_model=str / bytes are documented as text/plain / application/octet-stream, matching what the serializer actually sends (was always application/json). For str | None / bytes | None both media types are documented: None is serialized as a JSON null
  • Non-standard status codes (e.g. 299, 499) no longer crash schema generation — they get a generic Status <code> description; a duplicate explicit operationId is reported with a warning instead of silently producing an invalid schema
  • Route decorators preserve the endpoint's type for type checkers ((F) -> F instead of Any), so py.typed consumers 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_filename probing 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 via model_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] and Model | None annotations work as body parameters without an explicit Body() marker
  • DjangoAsyncRouter with an explicit OPTIONS route no longer returns 500 (Django's view_is_async ignores the options handler)
  • 204/304 responses keep custom headers on every adapter (rc1 fixed only some); Tornado no longer crashes with an AssertionError on 304
  • Dependency resolution memory leak fixed: signature caches no longer grow per request; cross-request dependency execution locks removed (restores concurrency in threaded WSGI)
  • Response objects and tuples returned from endpoints with response_model no longer fail validation — explicit responses skip it, as in FastAPI
  • Nested model $defs are registered in components/schemas (no more broken $refs from models expanded into query parameters)
  • Mixed-type lists in responses no longer crash serialization

Removed

  • PaginationParams schema is no longer injected into every generated components/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.media for all)
  • Async DI resolver no longer holds threading.Lock across await — removed unnecessary lock since async is single-threaded per event loop
  • Tornado cookies now extracted as strings instead of Morsel objects
  • 204 responses now preserve custom headers from tuple returns and Response objects
  • 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 Depends and Security for automatic dependency resolution
  • Request-scoped caching (same dependency called multiple times returns cached result)
  • Circular dependency detection
  • SecurityScopes injection 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) and DjangoAsyncRouter (async), including urls property for Django URL patterns
  • Falcon async support with FalconAsyncRouter (in addition to existing sync FalconRouter)
  • FileUpload class for framework-agnostic file handling with .read() and .aread() methods
  • Form data and file upload extraction for all frameworks
  • RequestData unified container for request data across all frameworks
  • Response class for custom responses with headers and status codes
  • Response model validation via TypeAdapter with 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 format
  • EXCEPTION_MAPPER on routers for framework-specific exception conversion (e.g., Django's PermissionDeniedAuthorizationError)
  • Built-in OpenAPI security schemes: Bearer JWT, API Key (header/query), Basic Auth, OAuth2
  • Custom security schemes via security_scheme parameter (accepts SecuritySchemeType enum or raw dict)
  • Security scheme merging in include_router()
  • SecuritySchemeType exported from fastopenapi for public use
  • Documentation completely rewritten with guides, API reference, framework-specific pages, and examples

Changed

  • Complete architecture refactor from monolithic base_router.py to composition-based modular design:
  • core/BaseRouter, parameter classes, dependency resolver, types, constants
  • resolution/ParameterResolver (extracted from BaseRouter.resolve_endpoint_params())
  • response/ResponseBuilder (extracted from BaseRouter._serialize_response())
  • openapi/OpenAPIGenerator, SchemaBuilder, UI renderers (extracted from BaseRouter.generate_openapi())
  • errors/ — error hierarchy (extracted from error_handler.py)
  • routers/BaseAdapter + per-framework packages with separate extractors
  • All framework routers now inherit from BaseAdapter instead of BaseRouter
  • Each framework router split into separate router and extractor modules
  • Route metadata stored in RouteInfo class (was tuple)
  • Validation errors now return HTTP 422 (was 400)
  • OpenAPI summary resolved from route metadata or formatted endpoint name; description from metadata or docstring
  • Improved import errors with MissingRouter raising ImportError when framework is not installed
  • django added as optional dependency extra

Deprecated

  • Importing from fastopenapi.error_handler module (use from fastopenapi.errors import ... instead)

Removed

  • BaseRouter.generate_openapi() method (use router.openapi property)
  • BaseRouter.resolve_endpoint_params() and BaseRouter._serialize_response() internal methods
  • Multi-language documentation (single English version retained)

[0.7.0] - 2025-04-27

Changed

  • Replaced json.dumps/json.loads with pydantic_core to_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_aliases from BaseRouter and reverted changes from 0.6.0.

[0.6.0] – 2025‑04‑16

Added

  • The use_aliases parameter was added to the BaseRouter constructor. Default is True. To preserve the previous behavior (without using aliases from Pydantic), set use_aliases=False.

Changed

  • The _serialize_response method is now an instance method (was a @staticmethod) — to support use_aliases.
  • The _get_model_schema method was temporarily changed from a @classmethod to a regular method — for consistent behavior with use_aliases.

Deprecated

  • use_aliases is 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_errors parameter for route decorators to document error responses in OpenAPI.
  • error_handler module for standard error responses (provides exceptions like BadRequestError, ResourceNotFoundError, etc., as described in documentation).
  • Support for using basic Python types (int, float, bool, str) as response_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_route and add_openapi_route methods from BaseRouter. 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 ModuleNotFoundError if 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_response now correctly handles BaseModel instances by converting them to dict before JSON encoding (preventing a TypeError).
  • Resolved a bug causing DataLoader to 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.md file to track changes.

[0.2.0] - 2025-03-11

Added

  • Implemented resolve_endpoint_params in BaseRouter to systematically resolve function parameters (path, query, body) and integrate with Pydantic validation.
  • Added prefix parameter to the include_router method, allowing grouping routes under a path.
  • Implemented status_code support 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_routes method 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.