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.

Why is this an issue?

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.

What is the potential impact?

Using this old syntax makes code less maintainable in several ways:

How to fix it in FastAPI

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.

Code examples

Noncompliant code example

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}

Compliant solution

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.

Noncompliant code example

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/search/")
def search_items(
    q: str = Query("latest", max_length=50)  # Noncompliant
):
    return {"query": q}

Compliant solution

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.

Noncompliant code example

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

Compliant solution

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

Resources

Documentation