committed by
GitHub
4 changed files with 328 additions and 204 deletions
@ -0,0 +1,93 @@ |
|||
# 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 [] |
|||
``` |
|||
@ -0,0 +1,93 @@ |
|||
# 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. |
|||
@ -0,0 +1,79 @@ |
|||
# 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 |
|||
``` |
|||
Loading…
Reference in new issue