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.

When dependencies are declared as default values, the actual type of the parameter becomes ambiguous. For example, db = Depends(get_db) doesn’t clearly indicate what type db will be at runtime. This makes the code harder to read and reduces the effectiveness of IDE autocompletion and type checking tools.

The modern Annotated syntax separates the type information from the dependency declaration, making both aspects explicit. This approach provides better clarity about what the parameter actually is, while still specifying how it should be injected.

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 the deprecated 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.

Code examples

Noncompliant code example

from fastapi import Depends, FastAPI

app = FastAPI()

def get_db():
    return "database_connection"

@app.get("/items/")
def read_items(db = Depends(get_db)):  # Noncompliant
    return {"db": db}

Compliant solution

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()

def get_db():
    return "database_connection"

@app.get("/items/")
def read_items(db: Annotated[str, Depends(get_db)]):
    return {"db": db}

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(None, 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 | None, Query(max_length=50)] = None
):
    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