Hello! Welcome to the final lesson in our module on FastAPI's Dependency Injection system.
In our last session, we created dependencies with yield to manage resource lifecycles, ensuring that actions like closing a database connection happen reliably. We saw that to use our get_db_session dependency, we had to add it to the signature of every path operation function that needed it. While explicit, this can become repetitive.
Today, we'll solve that problem by addressing the learning outcome: Apply dependencies at different scopes: globally (app), on a router, or for a specific path operation. You'll learn how to apply a dependency once and have it affect an entire group of endpoints, or even your entire application.
This is a powerful feature for enforcing broad rules like authentication, logging, or API key validation without cluttering your business logic. For you, coming from Django, this is analogous to the different ways you can apply logic:
- A decorator on a single function-based view (
@login_required). - Applying middleware to your entire project in
settings.py(MIDDLEWARElist). - Applying a mixin or
permission_classesto a DRFViewSet, which affects all actions within it.
FastAPI gives you this same flexibility but through its unified dependency injection system.
1. Path Operation Dependencies: Two Ways
So far, we've only injected dependencies via the path operation function's signature:
@app.get("/items/{item_id}")
async def read_item(item: Annotated[Item, Depends(get_item_from_db)]):
return item
Here, the value returned by get_item_from_db is injected and available as the item variable.
However, some dependencies don't need to return a value. Their only job is to perform a check and raise an exception if it fails (e.g., check an API key, verify a permission). For these "side-effect" dependencies, you can add them to the decorator itself.
The key rule to remember is this:
- Dependencies in the function signature can
returnoryielda value that gets injected into your endpoint logic. - Dependencies in the
dependenciesparameter of a decorator, router, or app run for their side effects only. Their return value is ignored.
This distinction is crucial for clean API design. Let's start with a resource that explains this and demonstrates adding dependencies to the path decorator.
How to Implement Dependency Injection in FastAPI - freeCodeCamp
The article 'How to Implement Dependency Injection in FastAPI' from freeCodeCamp provides a great overview of dependency scopes. We'll start with the section on the path operation level.
Please read the sections 'Dependency Scope' and 'Path Operation Level'. Focus on the distinction between injecting as a function parameter versus using the dependencies parameter in the decorator. The example of the user_dep function is a perfect illustration of a dependency used for its side effect (authentication).
As the article shows, you can move a dependency like an authentication check from the function signature into a list in the decorator:
async def verify_api_key(x_api_key: Annotated[str, Header()]):
if x_api_key != "fake-super-secret-key":
raise HTTPException(status_code=403, detail="Invalid API Key")
@app.get("/users/me", dependencies=[Depends(verify_api_key)])
async def read_current_user():
return {"user_id": "the_current_user"}
Notice that read_current_user is now cleaner. The verify_api_key dependency runs first, but its return value (which is None anyway) is not passed into the function.
2. Router-Level Dependencies
Applying a dependency to a single decorator is useful, but the real power comes when you apply it to a group of related routes. This is where APIRouter comes in. Just as you group URLs in a urls.py file for a Django app, you group path operations with APIRouter in FastAPI. You can add a dependencies list to the router, and it will be applied to every path operation defined on it.
This is ideal for securing an entire section of your API, for instance, requiring authentication for all endpoints under /admin or /profile.
This architectural pattern aligns with your goal of building modern, scalable applications. By grouping related endpoints in routers, you create modular and maintainable code.

Let's see how to implement this.
How to Implement Dependency Injection in FastAPI - freeCodeCamp
Let's return to the freeCodeCamp article, which has a clear section on router-level dependencies.
Read the section 'Router Level'. It demonstrates how to create an APIRouter instance and pass the dependencies list to it, and then how to include that router in your main application.
Here's a practical example building on that:
from fastapi import APIRouter, Depends, Header, HTTPException
from typing import Annotated
# This router will contain all admin-related endpoints
admin_router = APIRouter(
prefix="/admin",
tags=["admin"],
dependencies=[Depends(verify_api_key)], # Applied to all routes in this router
responses={404: {"description": "Not found"}},
)
@admin_router.get("/dashboard")
async def admin_dashboard():
return {"message": "Welcome to the admin dashboard!"}
@admin_router.post("/users")
async def create_user():
return {"message": "User created successfully."}
# In your main app file:
# app.include_router(admin_router)
Now, both /admin/dashboard and /admin/users are automatically protected by verify_api_key without you having to specify it on each one.
3. Global Dependencies (Application-Level)
Finally, what if a dependency needs to run for every single request your application receives? This is perfect for tasks like logging every request, adding a unique request ID to headers, or performance monitoring.
In Django, this is the job of middleware. In FastAPI, you can achieve the same effect by adding a dependency directly to your main FastAPI app instance. These are called global dependencies.
The following video and article explain how to set this up.
Fast API Tutorial, Part 25: Dependencies in path operation decorators, global dependencies
This video tutorial from JVP Design clearly demonstrates moving from decorator-level dependencies to global dependencies.
Watch the segment from 05:11 to 11:21. The first part (until 08:50) reinforces decorator-level dependencies. The second part (from 08:50 onwards) shows exactly how to add a dependencies list to the FastAPI() app instance to make them global.
How to Implement Dependency Injection in FastAPI - freeCodeCamp
The freeCodeCamp article also provides a concise example of an application-level dependency for logging.
Quickly read the 'Application Level' section to see another example of a global dependency.
Here's how you'd implement a simple global logger:
from fastapi import FastAPI, Request
import time
async def log_requests(request: Request):
print(f"Incoming request: {request.method} {request.url.path}")
start_time = time.time()
# This dependency doesn't have a `yield`, so the code after this
# won't run. For timing, you'd typically use middleware.
# This is just for simple logging at the start of a request.
app = FastAPI(dependencies=[Depends(log_requests)])
@app.get("/")
async def root():
return {"message": "Hello World"}
With this setup, every single API call, no matter the path, will trigger the log_requests function.
Summary of Scopes
Let's consolidate everything into a single example demonstrating all three scopes working together.
import time
from typing import Annotated
from fastapi import APIRouter, Depends, FastAPI, Header, HTTPException, Request
# --- Dependencies ---
async def log_requests(request: Request):
"""Global dependency to log all incoming requests."""
print(f"GLOBAL LOG: {request.method} {request.url.path}")
async def verify_token(x_token: Annotated[str, Header()]):
"""Router-level dependency to check a token."""
if x_token != "fake-secret-token":
raise HTTPException(status_code=403, detail="X-Token header invalid")
print("ROUTER-LEVEL: Token verified.")
async def verify_key(x_key: Annotated[str, Header()]):
"""Path-level dependency to check a key."""
if x_key != "fake-secret-key":
raise HTTPException(status_code=403, detail="X-Key header invalid")
print("PATH-LEVEL: Key verified.")
return x_key # Return value is discarded since it's in `dependencies`
# --- Global App ---
app = FastAPI(dependencies=[Depends(log_requests)])
# --- Router for "Items" ---
router = APIRouter(
prefix="/items",
dependencies=[Depends(verify_token)]
)
@router.get("/")
async def read_items():
return [{"name": "Foo"}, {"name": "Bar"}]
@router.get(
"/{item_id}",
dependencies=[Depends(verify_key)] # Additional dependency for this specific path
)
async def read_item(item_id: str):
return {"name": f"Item {item_id}"}
app.include_router(router)
@app.get("/status")
async def get_status():
"""An endpoint not in the router, only subject to global dependencies."""
return {"status": "ok"}
If you run this code and make a request to /items/123 with the correct headers, your console will show:
GLOBAL LOG: GET /items/123ROUTER-LEVEL: Token verified.PATH-LEVEL: Key verified.
This clearly demonstrates the hierarchical application of dependencies from the broadest scope to the most specific.
Conclusion
You have now completed your tour of FastAPI's Dependency Injection system. By learning to apply dependencies at different scopes, you can write much cleaner, more secure, and more maintainable APIs.
Key Takeaways:
- Three Scopes: Dependencies can be applied at the Global (app), Router (
APIRouter), and Path Operation Decorator levels. - Side-Effect vs. Value Injection: Dependencies placed in the
dependencies=[...]list are for side effects (like validation) and their return values are ignored. Dependencies in a function's signature inject their return value into the endpoint. - DRY Principle: Scoped dependencies are a powerful tool for Don't Repeat Yourself (DRY), centralizing common logic like authentication, logging, and permissions.
- Architectural Analogy: This system provides a unified way to handle logic that in Django might be split between
settings.MIDDLEWARE,urls.pyincludes, and view decorators/mixins.
Preview of the Next Module:
Our next module, "Authentication and Authorization," will put everything we've just learned into practice. We will build a robust security system for our API, and scoped dependencies will be the core mechanism we use. We will create dependencies to hash passwords, generate JWTs, and protect entire groups of routes by applying an authentication dependency to a router. This lesson was the perfect bridge to building a truly professional and secure application.