This is an issue when a FastAPI route decorator declares path parameters (e.g., @app.get("/items/{item_id}")) but neither the route
handler, called a path operation function in FastAPI, nor its dependencies declare those parameters as arguments. It is also an issue when path
parameters are declared as positional-only arguments (using / in the signature).
FastAPI is a modern web framework that uses Python type hints and function signatures to automatically extract, validate, and inject request parameters into route handlers and dependencies. FastAPI injects path values only into parameters with matching names declared by the path operation function or by one of its dependencies.
Path parameters are segments of the URL path that are used to identify specific resources. For example, in the route /items/{item_id},
the item_id is a path parameter that would capture values like 123 from a request to /items/123.
The framework works by inspecting function signatures at startup and creating a mapping between route path parameters and path operation function or dependency parameters. When a request comes in, FastAPI extracts the values from the URL path and injects them into matching parameters by name.
When path parameters are not declared by the path operation function or its dependencies, several problems can occur:
Additionally, if a path parameter is declared as a positional-only argument (using / in the function signature), FastAPI cannot inject
it because the framework uses keyword arguments for parameter injection. This is a subtle but important constraint of how FastAPI’s dependency
injection system works.
This type of error is problematic because it may not be caught during development if the specific route is not tested, leading to validation errors or incorrect endpoint behavior in production.
When path parameters are not declared by the path operation function or its dependencies, the endpoint contract no longer matches the code that handles the request. This can cause:
The impact depends on how the path operation function is written. Some mismatches cause immediate request failures, while others silently produce stale or misleading API behavior.
Include all path parameters from the route decorator in the path operation function signature, or in the signature of a FastAPI dependency used by that route. The parameter names must match the parameter names in the route path (enclosed in curly braces). Add type hints to enable FastAPI’s automatic validation and conversion.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(): # Noncompliant: item_id is missing
return {"message": "Hello"}
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}
When you have multiple path parameters, include all of them in the function signature. The order of parameters in the function signature does not need to match the order in the path, but all path parameters must be present.
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}/items/{item_id}")
def read_user_item(user_id: int): # Noncompliant: item_id is missing
return {"user_id": user_id}
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}/items/{item_id}")
def read_user_item(user_id: int, item_id: int):
return {"user_id": user_id, "item_id": item_id}
Avoid declaring path parameters as positional-only arguments (before the / separator). FastAPI injects parameters as keyword
arguments, so positional-only parameters cannot be injected.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int, /): # Noncompliant: positional-only parameter
return {"item_id": item_id}
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}
Path parameters can be combined with query parameters and request body parameters. Ensure all path parameters are included in the signature, regardless of what other parameters are present.
from fastapi import FastAPI
app = FastAPI()
@app.get("/things/{thing_id}")
async def read_thing(query: str): # Noncompliant: thing_id is missing
return {"query": query}
from fastapi import FastAPI
app = FastAPI()
@app.get("/things/{thing_id}")
async def read_thing(thing_id: int, query: str):
return {"thing_id": thing_id, "query": query}