Safely Sharing FastAPI Dependencies Across Multiple Routers

FastAPI’s dependency injection system is powerful, but as your application grows and you modularize your code, it’s common to split your API into multiple routers. A question I often get is: "How can I safely share dependencies (like authentication or database access) across routers without duplicating code or causing unexpected side effects?"

Let’s walk through best practices for sharing dependencies among FastAPI routers, as well as some caveats to avoid.


Why Router-Level Dependencies Matter

Assigning dependencies at the router level allows you to:

  1. Reduce duplication: You don’t need to declare the same dependency on every single endpoint.
  2. Enforce security or shared resources: For example, you can ensure all routes under /admin require certain authentication, or that all endpoints under /v1 share caching or throttling logic.

Defining and Sharing Dependencies

Let’s say you have an authentication dependency:

def get_current_user(token: str = Depends(oauth2_scheme)):
    # ... authentication logic ...
    return user

And two routers, items and orders, that should require authentication on all endpoints.

Set the dependency at router creation:

from fastapi import APIRouter, Depends

items_router = APIRouter(
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_current_user)]
)

orders_router = APIRouter(
    prefix="/orders",
    tags=["orders"],
    dependencies=[Depends(get_current_user)]
)

Now, every endpoint included via these routers will require your get_current_user dependency.


Caveats and Best Practices

  • Don’t Repeat Dependency Objects: Use the same dependency object, not a copy. That means always refer to the same function (not a redefined lambda, etc.).

  • Composable Dependencies: If you need both shared (e.g., authentication) and endpoint-specific (e.g., role verification) dependencies, let endpoint dependencies depend-on the router dependency:

    def get_admin_user(user: User = Depends(get_current_user)):
        if not user.is_admin:
            raise HTTPException(...)
        return user
    
  • Custom Dependency Classes: For shared state or configuration, custom classes can encapsulate logic and can be instantiated globally, then used as a dependency.


What About Dependency Caching?

Dependency caching (using Depends(..., use_cache=True), which is the default) ensures that, even if a dependency is listed as a router dependency and again on an endpoint, FastAPI will only call it once per request – no wasted effort or mismatched context.


Summary

To efficiently share dependencies across routers in FastAPI:

  • Declare them as global (module-level) functions or classes.
  • Attach them to routers via the dependencies argument.
  • Let router dependencies provide broad concerns (auth, DB), and supplement per-endpoint as needed.

This organization keeps your code DRY, your logic maintainable, and your APIs scalable as they grow!

Happy coding!

— Fast Eddy

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *