Factory.ai

Open-Source Wikis

/

FastAPI

/

How to contribute

/

Patterns and conventions

fastapi/fastapi

Patterns and conventions

Type style

pyproject.toml enables tool.mypy with strict = true and the pydantic.mypy plugin. Code in fastapi/ is fully type-annotated. Two style points:

  • Annotated[T, Doc(...)] for public callables. Most parameters in fastapi/param_functions.py, fastapi/applications.py, and the security classes use Annotated[T, Doc("""...""")] from the annotated-doc package. The Doc(...) payload is a markdown string that mkdocstrings renders into the API reference. Example from fastapi/datastructures.py:

    filename: Annotated[str | None, Doc("The original file name.")]
  • PEP 604 unions. requires-python = ">=3.10", so the source uses str | None rather than Optional[str].

Public API discipline

fastapi/__init__.py is the only module user code is meant to import from. Every name there is from … import X as X — the as X rebinding silences F401 and makes the export intentional. When adding a new symbol, decide whether it belongs in the public surface or behind a sub-module like fastapi.security or fastapi.encoders. Removing or renaming anything in fastapi/__init__.py is a breaking change.

Default placeholders

fastapi/datastructures.py:DefaultPlaceholder and the helper Default(value) exist so the framework can tell "user passed None" apart from "user passed nothing." Always wrap framework-supplied defaults with Default(...). To check, prefer isinstance(x, DefaultPlaceholder) over equality.

get_value_or_default(...) in fastapi/utils.py walks a chain of values and returns the first non-placeholder.

_compat boundary

Anything that depends on Pydantic internals goes through fastapi/_compat. Direct imports of pydantic.fields.FieldInfo are common (see fastapi/params.py), but utilities like get_definitions, get_schema_from_model_field, create_body_model, and the ModelField wrapper are imported from fastapi._compat. This keeps Pydantic-version drift containable.

Error handling

  • Raise HTTPException (fastapi/exceptions.py) for client-visible errors. The default handler is registered in FastAPI.__init__; do not register one yourself unless overriding behaviour.
  • Raise FastAPIError for framework-internal misuse. Subclass it (DependencyScopeError, PydanticV1NotSupportedError) when a specific case needs to be catchable.
  • Validation errors (RequestValidationError, ResponseValidationError, WebSocketRequestValidationError) carry an optional endpoint_ctx: EndpointContext so error messages name the offending endpoint. When you build them, plumb the context through.

Async vs sync callables

The framework supports both. The Dependant model caches is_coroutine_callable, is_gen_callable, is_async_gen_callable as @cached_property — never compute these inline in a hot path. Sync functions are dispatched to a threadpool via starlette.concurrency.run_in_threadpool. See request_response() in fastapi/routing.py.

Decorator-driven registration

The framework relies on decorators that do work at import time. Keep decorator implementations short — most of the heavy lifting in APIRouter.add_api_route happens during add_api_route, not during @app.get(...). The decorator returns the same callable so users can still call it directly in tests.

Re-exports of Starlette

When a Starlette type is part of the FastAPI public API, re-export it through a one-line shim (see fastapi/staticfiles.py, fastapi/templating.py, fastapi/testclient.py). Do not import from starlette.* in user-facing examples — point users at fastapi.*.

Deprecation pattern

Deprecate through typing_extensions.deprecated (or pydantic.deprecated.*) decorators applied directly to the parameter or function. Issue FastAPIDeprecationWarning (a subclass of UserWarning — Python ignores DeprecationWarning by default in libraries; see the docstring in fastapi/exceptions.py). Examples:

  • regex= parameter on Param.__init__ (fastapi/params.py).
  • UJSONResponse and ORJSONResponse (fastapi/responses.py).
  • generate_operation_id_for_path (fastapi/utils.py).

Lint rules

tool.ruff.lint.select = ["E", "W", "F", "I", "B", "C4", "UP"]. Notable per-file exceptions live in tool.ruff.lint.per-file-ignores — most are tutorial examples that intentionally show "wrong" code (e.g. unused variables, shadowed names) for pedagogy. Add to that list rather than silencing rules globally.

Test conventions

See Testing for the structural conventions. The two most-followed rules:

  1. Every behaviour change in fastapi/ ships with a regression test in tests/.
  2. Every new tutorial in docs_src/ ships with a corresponding test under tests/test_tutorial/. The naming convention is mechanical (docs_src/foo/tutorial001.pytests/test_tutorial/test_foo/test_tutorial001.py).

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – FastAPI wiki | Factory