This rule raises an issue when FastAPI route handler parameters use Depends(), Query(), Path(),
Body(), or similar dependency injection functions as default parameter values instead of within Annotated type hints.
FastAPI originally allowed declaring dependencies by using them as default parameter values. While this approach still works, it has several drawbacks that make code harder to maintain and understand.
Using FastAPI functions such as Depends(), Query(), Path(), and Body() as parameter default
values mixes two different concerns: the parameter’s actual Python default value and FastAPI’s request handling metadata.
The modern Annotated syntax keeps the type and any real Python default value in their usual places, while still making the
FastAPI-specific behavior explicit.
Using Annotated consistently across your codebase also aligns with FastAPI’s current recommendations and ensures your code follows
modern Python typing conventions introduced in Python 3.9.
The old syntax mixes concerns by using the default value mechanism for dependency injection, which is semantically confusing since the "default" isn’t really a default value in the traditional sense.
Using this old syntax makes code less maintainable in several ways:
Replace dependency declarations in default values with Annotated type hints. Import Annotated from the
typing module (Python 3.9+) or typing_extensions (Python 3.8 and earlier). The first argument to Annotated is
the actual type of the parameter, and the second argument is the dependency injection specification. If the parameter has a real default value, keep
it as the normal Python default after the annotation.
from dataclasses import dataclass
from fastapi import Depends, FastAPI
app = FastAPI()
@dataclass
class User:
username: str
def get_current_user() -> User:
return User(username="alice")
@app.get("/items/")
def read_items(user = Depends(get_current_user)): # Noncompliant
return {"user": user.username}
from dataclasses import dataclass
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
@dataclass
class User:
username: str
def get_current_user() -> User:
return User(username="alice")
@app.get("/items/")
def read_items(user: Annotated[User, Depends(get_current_user)]):
return {"user": user.username}
The same pattern applies to query parameters, path parameters, and request body parameters. Use Annotated to combine the type with the
parameter specification.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/search/")
def search_items(
q: str = Query("latest", max_length=50) # Noncompliant
):
return {"query": q}
from typing import Annotated
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/search/")
def search_items(
q: Annotated[str, Query(max_length=50)] = "latest"
):
return {"query": q}
For complex dependencies that return specific types, specify the return type explicitly in the Annotated declaration to improve type
safety.
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0):
return {"q": q, "skip": skip}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)): # Noncompliant
return commons
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str | None = None, skip: int = 0):
return {"q": q, "skip": skip}
@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons