Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
8cfaaddc7a ⬆ Bump CodSpeedHQ/action in the github-actions group
Bumps the github-actions group with 1 update: [CodSpeedHQ/action](https://github.com/codspeedhq/action).


Updates `CodSpeedHQ/action` from 4.17.0 to 4.17.5
- [Release notes](https://github.com/codspeedhq/action/releases)
- [Changelog](https://github.com/CodSpeedHQ/action/blob/main/CHANGELOG.md)
- [Commits](9d332c4d90...c145068895)

---
updated-dependencies:
- dependency-name: CodSpeedHQ/action
  dependency-version: 4.17.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-23 11:45:17 +00:00
13 changed files with 247 additions and 356 deletions

View File

@@ -4,6 +4,10 @@ on:
branches:
- master
pull_request:
types:
- opened
- synchronize
permissions: {}
jobs:

View File

@@ -29,6 +29,29 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: tiangolo/issue-manager@75d60679db1ea348f6f6ea1d0e20de80a7c04645 # 0.7.1
- uses: tiangolo/issue-manager@2fb3484ec9279485df8659e8ec73de262431737d # 0.6.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
config: >
{
"answered": {
"delay": 864000,
"message": "Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs."
},
"waiting": {
"delay": 2628000,
"message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR.",
"reminder": {
"before": "P3D",
"message": "Heads-up: this will be closed in 3 days unless there's new activity."
}
},
"invalid": {
"delay": 0,
"message": "This was marked as invalid and will be closed now. If this is an error, please provide additional details."
},
"maybe-ai": {
"delay": 0,
"message": "This was marked as potentially AI generated and will be closed now. If this is an error, please provide additional details, make sure to read the docs about contributing and AI."
}
}

View File

@@ -2,6 +2,10 @@ name: pre-commit
on:
pull_request:
types:
- opened
- synchronize
permissions: {}
env:

View File

@@ -5,6 +5,10 @@ on:
branches:
- master
pull_request:
types:
- opened
- synchronize
permissions: {}
jobs:

View File

@@ -5,6 +5,9 @@ on:
branches:
- master
pull_request:
types:
- opened
- synchronize
schedule:
# cron every week on monday
- cron: "0 0 * * 1"
@@ -191,7 +194,7 @@ jobs:
- name: Install Dependencies
run: uv sync --no-dev --group tests --extra all
- name: CodSpeed benchmarks
uses: CodSpeedHQ/action@9d332c4d90b43981c3e55ae8e38e68709996240f # v4.17.0
uses: CodSpeedHQ/action@c145068895e045cc725ee76fcd2307624b65c3af # v4.17.5
with:
mode: simulation
run: uv run --no-sync pytest tests/benchmarks --codspeed

View File

@@ -53,6 +53,3 @@ bronze:
# - url: https://testdriven.io/courses/tdd-fastapi/
# title: Learn to build high-quality web apps with best practices
# img: /img/sponsors/testdriven.svg
- url: https://www.testmu.ai/?utm_source=fastapi&utm_medium=partner&utm_campaign=sponsor&utm_term=opensource&utm_content=webpage
title: TestMu AI. The Native AI-Agentic Cloud Platform to Supercharge Quality Engineering.
img: /img/sponsors/testmu.png

View File

@@ -249,16 +249,6 @@ They are supporting my work with **FastAPI** (and others), mainly through [GitHu
<a href="{{ sponsor.url }}" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
{% if sponsors.bronze %}
### Bronze Sponsors
{% for sponsor in sponsors.bronze -%}
<a href="{{ sponsor.url }}" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
{% endif %}
### Individual Sponsors

View File

@@ -7,18 +7,8 @@ hide:
## Latest Changes
## 0.138.1 (2026-06-25)
### Refactors
* ♻️ Refactor Library Skills, make info easier to find for agents. PR [#15841](https://github.com/fastapi/fastapi/pull/15841) by [@tiangolo](https://github.com/tiangolo).
### Internal
* 👷 Simplify pull request workflow triggers. PR [#15836](https://github.com/fastapi/fastapi/pull/15836) by [@tiangolo](https://github.com/tiangolo).
* 👷 Update issue-manager to 0.7.1. PR [#15833](https://github.com/fastapi/fastapi/pull/15833) by [@tiangolo](https://github.com/tiangolo).
* ⬆️ Update issue-manager to 0.7.0. PR [#15831](https://github.com/fastapi/fastapi/pull/15831) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update sponsors: Add TestMu again. PR [#15830](https://github.com/fastapi/fastapi/pull/15830) by [@tiangolo](https://github.com/tiangolo).
* 🔒️ Update zizmor workflow security checks. PR [#15820](https://github.com/fastapi/fastapi/pull/15820) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump pydantic-settings from 2.14.1 to 2.14.2. PR [#15799](https://github.com/fastapi/fastapi/pull/15799) by [@dependabot[bot]](https://github.com/apps/dependabot).

View File

@@ -1,23 +1,12 @@
---
name: fastapi
description: FastAPI best practices and conventions. Use when working with FastAPI APIs, Pydantic models, dependencies, streaming responses including Server-Sent Events (SSE), and serving frontend apps. Keeps FastAPI code clean and up to date with the latest features and patterns.
description: FastAPI best practices and conventions. Use when working with FastAPI APIs and Pydantic models for them. Keeps FastAPI code clean and up to date with the latest features and patterns, updated with new versions. Write new code or refactor and update old code.
---
# FastAPI
Official FastAPI skill to write code with best practices, keeping up to date with new versions and features.
## Quick Reference
* Serve frontend apps: use `app.frontend()` or `router.frontend()` for built frontend assets; see [Serve Frontend Apps](#serve-frontend-apps).
* Server-Sent Events (SSE): use `response_class=EventSourceResponse` and `yield`; see [Streaming](#streaming-json-lines-sse-bytes) and [the streaming reference](references/streaming.md).
* JSON Lines and byte streaming: see [the streaming reference](references/streaming.md).
* Dependencies: use `Annotated[..., Depends(...)]`; see [Dependency Injection](#dependency-injection) and [the dependency injection reference](references/dependencies.md) for `yield`, scopes, and class dependencies.
* Response models: prefer return types; use `response_model` when the public response schema differs from the internal return value; see [the response reference](references/responses.md).
* Pydantic models: do not use ellipsis or `RootModel`; see [the Pydantic reference](references/pydantic.md).
* Routing: declare router-level prefix, tags, and shared dependencies on the `APIRouter`; see [the path operation reference](references/path-operations.md).
* Tooling and related libraries: use uv, Ruff, ty, Asyncer, SQLModel, and HTTPX when applicable; see [the other tools reference](references/other-tools.md).
## Use the `fastapi` CLI
Run the development server on localhost with reload:
@@ -26,28 +15,39 @@ Run the development server on localhost with reload:
fastapi dev
```
Run the production server:
```bash
fastapi run
```
Prefer declaring the entrypoint in `pyproject.toml`:
### Add an entrypoint in `pyproject.toml`
FastAPI CLI will read the entrypoint in `pyproject.toml` to know where the FastAPI app is declared.
```toml
[tool.fastapi]
entrypoint = "my_app.main:app"
```
When adding the entrypoint is not possible, or the user explicitly asks not to, pass the app file path:
### Use `fastapi` with a path
When adding the entrypoint to `pyproject.toml` is not possible, or the user explicitly asks not to, or it's running an independent small app, you can pass the app file path to the `fastapi` command:
```bash
fastapi dev my_app/main.py
```
Prefer to set the entrypoint in `pyproject.toml` when possible.
## Use `Annotated`
Always prefer the `Annotated` style for parameter and dependency declarations. It keeps function signatures working in other contexts, respects the types, and allows reusability.
Always prefer the `Annotated` style for parameter and dependency declarations.
It keeps the function signatures working in other contexts, respects the types, allows reusability.
### In Parameter Declarations
Use `Annotated` for parameter declarations, including `Path`, `Query`, `Header`, etc.:
@@ -67,7 +67,23 @@ async def read_item(
return {"message": "Hello World"}
```
Use `Annotated` for dependencies with `Depends()`. Unless asked not to, create a new type alias for the dependency to allow reusing it:
instead of:
```python
# DO NOT DO THIS
@app.get("/items/{item_id}")
async def read_item(
item_id: int = Path(ge=1, description="The item ID"),
q: str | None = Query(default=None, max_length=50),
):
return {"message": "Hello World"}
```
### For Dependencies
Use `Annotated` for dependencies with `Depends()`.
Unless asked not to, create a new type alias for the dependency to allow re-using it.
```python
from typing import Annotated
@@ -89,9 +105,20 @@ async def read_item(current_user: CurrentUserDep):
return {"message": "Hello World"}
```
instead of:
```python
# DO NOT DO THIS
@app.get("/items/")
async def read_item(current_user: dict = Depends(get_current_user)):
return {"message": "Hello World"}
```
## Do not use Ellipsis for *path operations* or Pydantic models
Do not use `...` as a default value for required parameters or model fields. It's not needed and not recommended.
Do not use `...` as a default value for required parameters, it's not needed and not recommended.
Do this, without Ellipsis (`...`):
```python
from typing import Annotated
@@ -99,8 +126,6 @@ from typing import Annotated
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
@@ -108,12 +133,29 @@ class Item(BaseModel):
price: float = Field(gt=0)
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item, project_id: Annotated[int, Query()]):
return item
async def create_item(item: Item, project_id: Annotated[int, Query()]): ...
```
See [the Pydantic reference](references/pydantic.md) for more details.
instead of this:
```python
# DO NOT DO THIS
class Item(BaseModel):
name: str = ...
description: str | None = None
price: float = Field(..., gt=0)
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item, project_id: Annotated[int, Query(...)]): ...
```
## Return Type or Response Model
@@ -136,9 +178,62 @@ async def get_item() -> Item:
return Item(name="Plumbus", description="All-purpose home device")
```
Return types or response models filter data to avoid exposing sensitive information, and they let Pydantic serialize the data on the Rust side for performance.
**Important**: Return types or response models are what filter data ensuring no sensitive information is exposed. And they are used to serialize data with Pydantic (in Rust), this is the main idea that can increase response performance.
Use `response_model` when the type you return is not the same as the public schema you want to validate, filter, document, and serialize. See [the response reference](references/responses.md).
The return type doesn't have to be a Pydantic model, it could be a different type, like a list of integers, or a dict, etc.
### When to use `response_model` instead
If the return type is not the same as the type that you want to use to validate, filter, or serialize, use the `response_model` parameter on the decorator instead.
```python
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
@app.get("/items/me", response_model=Item)
async def get_item() -> Any:
return {"name": "Foo", "description": "A very nice Item"}
```
This can be particularly useful when filtering data to expose only the public fields and avoid exposing sensitive information.
```python
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class InternalItem(BaseModel):
name: str
description: str | None = None
secret_key: str
class Item(BaseModel):
name: str
description: str | None = None
@app.get("/items/me", response_model=Item)
async def get_item() -> Any:
item = InternalItem(
name="Foo", description="A very nice Item", secret_key="supersecret"
)
return item
```
## Performance
@@ -148,23 +243,16 @@ Instead, declare a return type or response model. Pydantic will handle the data
## Including Routers
When declaring routers, prefer to add router-level parameters like prefix, tags, and shared dependencies to the router itself instead of in `include_router()`.
When declaring routers, prefer to add router level parameters like prefix, tags, etc. to the router itself, instead of in `include_router()`.
Do this:
```python
from fastapi import APIRouter, Depends, FastAPI
from fastapi import APIRouter, FastAPI
app = FastAPI()
def get_current_user():
return {"username": "johndoe"}
router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(get_current_user)],
)
router = APIRouter(prefix="/items", tags=["items"])
@router.get("/")
@@ -172,10 +260,33 @@ async def list_items():
return []
# In main.py
app.include_router(router)
```
See [the path operation reference](references/path-operations.md) for more routing patterns.
instead of this:
```python
# DO NOT DO THIS
from fastapi import APIRouter, FastAPI
app = FastAPI()
router = APIRouter()
@router.get("/")
async def list_items():
return []
# In main.py
app.include_router(router, prefix="/items", tags=["items"])
```
There could be exceptions, but try to follow this convention.
Apply shared dependencies at the router level via `dependencies=[Depends(...)]`.
## Serve Frontend Apps
@@ -205,15 +316,15 @@ app.include_router(router)
## Dependency Injection
Use dependencies when the logic can't be declared in Pydantic validation, depends on external resources, needs cleanup with `yield`, or is shared across endpoints.
See [the dependency injection reference](references/dependencies.md) for detailed patterns including `yield` with `scope`, and class dependencies.
Use dependencies when the logic can't be declared in Pydantic validation, depends on external resources, needs cleanup (with `yield`), or is shared across endpoints.
Apply shared dependencies at the router level via `dependencies=[Depends(...)]`.
See [the dependency injection reference](references/dependencies.md) for detailed patterns including `yield` with `scope`, and class dependencies.
## Async vs Sync *path operations*
Use `async` *path operations* only when fully certain that the logic called inside is compatible with async and await, and that it doesn't block.
Use `async` *path operations* only when fully certain that the logic called inside is compatible with async and await (it's called with `await`) or that it doesn't block.
```python
from fastapi import FastAPI
@@ -221,44 +332,30 @@ from fastapi import FastAPI
app = FastAPI()
# Use async def when calling async code
@app.get("/async-items/")
async def read_async_items():
data = await some_async_library.fetch_items()
return data
# Use plain def when calling blocking/sync code or when in doubt
@app.get("/items/")
def read_items():
data = some_blocking_library.fetch_items()
return data
```
In case of doubt, or by default, use regular `def` functions. They will be run in a threadpool so they don't block the event loop. The same rules apply to dependencies.
In case of doubt, or by default, use regular `def` functions, those will be run in a threadpool so they don't block the event loop.
Make sure blocking code is not run inside of `async` functions. The logic will work, but will damage performance heavily.
The same rules apply to dependencies.
Make sure blocking code is not run inside of `async` functions. The logic will work, but will damage the performance heavily.
When needing to mix blocking and async code, see Asyncer in [the other tools reference](references/other-tools.md).
## Streaming (JSON Lines, SSE, bytes)
To stream Server-Sent Events, use `response_class=EventSourceResponse` and `yield` items from the endpoint.
```python
from collections.abc import AsyncIterable
from fastapi import FastAPI
from fastapi.sse import EventSourceResponse, ServerSentEvent
app = FastAPI()
@app.get("/events", response_class=EventSourceResponse)
async def stream_events() -> AsyncIterable[ServerSentEvent]:
yield ServerSentEvent(data={"status": "started"}, event="status", id="1")
```
Plain objects are automatically JSON-serialized as `data:` fields. Use `ServerSentEvent` for full control over SSE fields (`event`, `id`, `retry`, `comment`) and `raw_data` for pre-formatted strings.
See [the streaming reference](references/streaming.md) for JSON Lines, Server-Sent Events (`EventSourceResponse`, `ServerSentEvent`), and byte streaming (`StreamingResponse`) patterns.
## Tooling
@@ -275,7 +372,9 @@ See [the other tools reference](references/other-tools.md) for details on other
## Do not use Pydantic RootModels
Do not use Pydantic `RootModel`; instead use regular type annotations with `Annotated` and Pydantic validation utilities.
Do not use Pydantic `RootModel`, instead use regular type annotations with `Annotated` and Pydantic validation utilities.
For example, for a list with validations you could do:
```python
from typing import Annotated
@@ -291,11 +390,35 @@ async def create_items(items: Annotated[list[int], Field(min_length=1), Body()])
return items
```
FastAPI supports these type annotations and will create a Pydantic `TypeAdapter` for them, so types work normally without custom wrapper models. See [the Pydantic reference](references/pydantic.md).
instead of:
```python
# DO NOT DO THIS
from typing import Annotated
from fastapi import FastAPI
from pydantic import Field, RootModel
app = FastAPI()
class ItemList(RootModel[Annotated[list[int], Field(min_length=1)]]):
pass
@app.post("/items/")
async def create_items(items: ItemList):
return items
```
FastAPI supports these type annotations and will create a Pydantic `TypeAdapter` for them, so that types can work as normally and there's no need for the custom logic and types in RootModels.
## Use one HTTP operation per function
Don't mix HTTP operations in a single function. Having one function per HTTP operation helps separate concerns and organize the code.
Don't mix HTTP operations in a single function, having one function per HTTP operation helps separate concerns and organize the code.
Do this:
```python
from fastapi import FastAPI
@@ -318,4 +441,22 @@ async def create_item(item: Item):
return item
```
See [the path operation reference](references/path-operations.md) for more examples.
instead of this:
```python
# DO NOT DO THIS
from fastapi import FastAPI, Request
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
@app.api_route("/items/", methods=["GET", "POST"])
async def handle_items(request: Request):
if request.method == "GET":
return []
```

View File

@@ -1,93 +0,0 @@
# Path Operations and Routing
## Including Routers
When declaring routers, prefer to add router-level parameters like prefix, tags, and shared dependencies to the router itself instead of in `include_router()`.
Do this:
```python
from fastapi import APIRouter, FastAPI
app = FastAPI()
router = APIRouter(prefix="/items", tags=["items"])
@router.get("/")
async def list_items():
return []
app.include_router(router)
```
Instead of:
```python
# DO NOT DO THIS
from fastapi import APIRouter, FastAPI
app = FastAPI()
router = APIRouter()
@router.get("/")
async def list_items():
return []
app.include_router(router, prefix="/items", tags=["items"])
```
There could be exceptions, but try to follow this convention.
Apply shared dependencies at the router level via `dependencies=[Depends(...)]`.
## Use one HTTP operation per function
Don't mix HTTP operations in a single function. Having one function per HTTP operation helps separate concerns and organize the code.
Do this:
```python
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
@app.get("/items/")
async def list_items():
return []
@app.post("/items/")
async def create_item(item: Item):
return item
```
Instead of:
```python
# DO NOT DO THIS
from fastapi import FastAPI, Request
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
@app.api_route("/items/", methods=["GET", "POST"])
async def handle_items(request: Request):
if request.method == "GET":
return []
```

View File

@@ -1,93 +0,0 @@
# Pydantic
## Do not use Ellipsis
Do not use `...` as a default value for required parameters or model fields. It's not needed and not recommended.
Do this, without Ellipsis (`...`):
```python
from typing import Annotated
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float = Field(gt=0)
@app.post("/items/")
async def create_item(item: Item, project_id: Annotated[int, Query()]):
return item
```
Instead of:
```python
# DO NOT DO THIS
from typing import Annotated
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str = ...
description: str | None = None
price: float = Field(..., gt=0)
@app.post("/items/")
async def create_item(item: Item, project_id: Annotated[int, Query(...)]):
return item
```
## Do not use Pydantic RootModels
Do not use Pydantic `RootModel`; instead use regular type annotations with `Annotated` and Pydantic validation utilities.
For example, for a list with validations:
```python
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import Field
app = FastAPI()
@app.post("/items/")
async def create_items(items: Annotated[list[int], Field(min_length=1), Body()]):
return items
```
Instead of:
```python
# DO NOT DO THIS
from typing import Annotated
from fastapi import FastAPI
from pydantic import Field, RootModel
app = FastAPI()
class ItemList(RootModel[Annotated[list[int], Field(min_length=1)]]):
pass
@app.post("/items/")
async def create_items(items: ItemList):
return items
```
FastAPI supports these type annotations and will create a Pydantic `TypeAdapter` for them, so types work normally without custom wrapper models.

View File

@@ -1,79 +0,0 @@
# Responses
## Return Type or Response Model
When possible, include a return type. It will be used to validate, filter, document, and serialize the response.
```python
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
@app.get("/items/me")
async def get_item() -> Item:
return Item(name="Plumbus", description="All-purpose home device")
```
Return types or response models filter data to avoid exposing sensitive information. They also let Pydantic serialize data on the Rust side for performance.
The return type doesn't have to be a Pydantic model. It can be a different type, like a list of integers, a dict, etc.
## When to use `response_model`
If the return type is not the same as the type that you want to use to validate, filter, or serialize, use the `response_model` parameter on the decorator.
```python
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
@app.get("/items/me", response_model=Item)
async def get_item() -> Any:
return {"name": "Foo", "description": "A very nice Item"}
```
This is particularly useful when filtering data to expose only the public fields and avoid exposing sensitive information.
```python
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class InternalItem(BaseModel):
name: str
description: str | None = None
secret_key: str
class Item(BaseModel):
name: str
description: str | None = None
@app.get("/items/me", response_model=Item)
async def get_item() -> Any:
item = InternalItem(
name="Foo", description="A very nice Item", secret_key="supersecret"
)
return item
```

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.138.1"
__version__ = "0.138.0"
from starlette import status as status