FastAPI Dependency Functions
In this tutorial, you'll learn about dependency functions in FastAPI - one of the framework's most powerful features that helps you build cleaner, more maintainable APIs.
Introduction to Dependency Functions
Dependency functions in FastAPI are regular Python functions that can be "injected" into path operation functions. They allow you to:
- Reuse code: Write functionality once and use it in multiple endpoints
- Share logic: Apply common processing steps across different routes
- Organize your code: Keep your endpoint functions clean and focused
- Handle cross-cutting concerns: Like authentication, database connections, and parameter validation
Think of dependencies as building blocks that you can combine and reuse throughout your application.
Basic Dependency Function
Let's start with a simple example:
from fastapi import Depends, FastAPI
app = FastAPI()
# This is our dependency function
def get_query_parameter(q: str = None):
return q or "No query parameter provided"
@app.get("/items/")
async def read_items(query_result: str = Depends(get_query_parameter)):
return {"query_result": query_result}
In this example:
- We define a dependency function
get_query_parameter
that processes a query parameter - We use
Depends(get_query_parameter)
to inject this function into our path operation - FastAPI calls the dependency function before the path operation
- The return value from the dependency is passed as the
query_result
parameter
When you visit /items/?q=test
, you'll get:
{
"query_result": "test"
}
And when you visit /items/
, you'll get:
{
"query_result": "No query parameter provided"
}
How Dependency Injection Works
When you use Depends()
, FastAPI does the following:
- It sees that your path operation function needs a parameter that is declared with
Depends()
- It calls the dependency function (passed to
Depends()
) with the appropriate parameters - It takes the return value from that function
- It assigns that return value to your path operation function parameter
Dependency Function Parameters
Dependency functions can have the same parameter types as path operation functions:
from fastapi import Depends, FastAPI, Header, HTTPException
app = FastAPI()
def verify_token(x_token: str = Header()):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
return x_token
def verify_key(x_key: str = Header()):
if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key
@app.get("/items/")
async def read_items(token: str = Depends(verify_token), key: str = Depends(verify_key)):
return {"token": token, "key": key}
In this example, both dependency functions check for specific headers and raise exceptions if they're not valid.
Nested Dependencies
A dependency can itself declare dependencies:
from fastapi import Depends, FastAPI
app = FastAPI()
def query_extractor(q: str = None):
return q or "default query"
def query_validator(q_or_default: str = Depends(query_extractor)):
if not q_or_default:
return "No query"
if q_or_default != "default query":
return q_or_default
return "default query (validated)"
@app.get("/items/")
async def read_items(final_query: str = Depends(query_validator)):
return {"query": final_query}
Here:
query_validator
depends onquery_extractor
- When
read_items
is called, FastAPI first callsquery_extractor
- Then it passes the result to
query_validator
- Finally, it passes the result of
query_validator
toread_items
Classes as Dependencies
You can also use classes as dependencies with the Depends
function:
from fastapi import Depends, FastAPI
app = FastAPI()
class CommonQueryParams:
def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
response = {}
if commons.q:
response.update({"q": commons.q})
return {
"response": response,
"skip": commons.skip,
"limit": commons.limit
}
This creates a dependency that:
- Receives query parameters
q
,skip
, andlimit
- Returns an instance of
CommonQueryParams
- Makes the instance available to the path operation function
Shortcut for Class Dependencies
FastAPI provides a shortcut for class-based dependencies. Instead of writing Depends(CommonQueryParams)
, you can write:
@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends()):
# ...
FastAPI will understand that it needs to use the CommonQueryParams
class itself as a dependency.
Dependency in Path Operation Decorators
You can add dependencies to the path operation decorator:
from fastapi import Depends, FastAPI, Header, HTTPException
app = FastAPI()
async def verify_token(x_token: str = Header()):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
@app.get("/items/", dependencies=[Depends(verify_token)])
async def read_items():
return [{"item": "Foo"}, {"item": "Bar"}]
Here, the dependency is executed before the path operation function but its value is not used in the function itself.
Real-World Example: Database Dependencies
One common use case for dependencies is handling database connections:
from fastapi import Depends, FastAPI
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, sessionmaker
# Database setup
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
app = FastAPI()
# Dependency function
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/users/")
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
# Here you would use the db session to query users
return [{"username": "user1"}, {"username": "user2"}]
In this example:
- We define a dependency function
get_db
that creates a database session - The
yield
statement returns the database session to the path operation function - After the path operation function completes, the code after the
yield
runs - This ensures the database connection is always closed, even if an exception occurs
Summary
FastAPI dependency functions provide a powerful way to:
- Reuse code across multiple endpoints
- Handle common functionality like authentication or database connections
- Keep your route handlers clean and focused on their specific tasks
- Build maintainable applications with separation of concerns
By mastering dependency injection, you'll be able to build more modular and maintainable FastAPI applications.
Further Learning
Here are some exercises to strengthen your understanding:
- Create a dependency that validates a custom header parameter
- Build a dependency chain with at least three levels of nested dependencies
- Implement a dependency that simulates rate limiting (returning an error if too many requests are made)
- Create a dependency that logs all incoming requests with their path and query parameters
Additional Resources
- FastAPI Official Documentation on Dependencies
- Advanced Dependencies in FastAPI
- Security and Authentication with FastAPI
Happy coding with FastAPI dependencies!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)