0
0
FastAPIframework~15 mins

Class-based dependencies in FastAPI - Deep Dive

Choose your learning style9 modes available
Overview - Class-based dependencies
What is it?
Class-based dependencies in FastAPI let you use a class to provide shared logic or data to your API endpoints. Instead of writing a function for dependency injection, you create a class with methods that FastAPI calls automatically. This helps organize code better and manage state or resources cleanly. It works by FastAPI creating an instance of your class and calling its __call__ method when needed.
Why it matters
Without class-based dependencies, you might write many separate functions that can get messy and hard to maintain, especially when sharing state or resources like database connections. Using classes groups related logic and data together, making your code cleaner and easier to understand. This also helps avoid repeating setup code and makes testing simpler. Without this, your API code can become tangled and error-prone.
Where it fits
Before learning class-based dependencies, you should understand basic FastAPI dependency injection using functions. After mastering this, you can explore advanced dependency features like dependency overrides, async dependencies, and integrating with external libraries or databases.
Mental Model
Core Idea
A class-based dependency is like a reusable tool box that FastAPI opens and uses whenever an endpoint needs it, keeping related setup and logic neatly packed together.
Think of it like...
Imagine a coffee machine that you program once with all settings and ingredients. Whenever you want coffee, you just press a button, and the machine uses its stored setup to make the drink. The class is the machine, and FastAPI pressing the button is calling the dependency.
┌─────────────────────┐
│   FastAPI Endpoint  │
└─────────┬───────────┘
          │ calls
          ▼
┌─────────────────────┐
│ Class-based Dependency│
│  ┌───────────────┐  │
│  │ __call__()    │  │
│  │ setup logic   │  │
│  │ shared state  │  │
│  └───────────────┘  │
└─────────────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding FastAPI dependencies
🤔
Concept: Learn what dependencies are in FastAPI and how they help share code between endpoints.
In FastAPI, dependencies are pieces of code that run before your endpoint function. They can provide data, check permissions, or set up resources. Usually, you write a function and use the Depends() helper to tell FastAPI to run it automatically. For example, a function that returns the current user or a database session.
Result
You can reuse code easily and keep your endpoints clean by separating concerns.
Understanding dependencies is key because class-based dependencies build on this idea but organize it differently.
2
FoundationBasic class structure for dependencies
🤔
Concept: Learn how to write a simple class that FastAPI can use as a dependency.
Create a class with a __call__ method. FastAPI treats this method like a function dependency. For example: class MyDependency: def __call__(self): return 'Hello from class' Use it in an endpoint: @app.get('/') async def root(dep: str = Depends(MyDependency())): return {'message': dep}
Result
FastAPI calls the class's __call__ method and injects its return value into the endpoint.
Knowing that __call__ makes a class instance behave like a function unlocks class-based dependencies.
3
IntermediateManaging state with class attributes
🤔Before reading on: Do you think class attributes keep their values between requests or reset each time? Commit to your answer.
Concept: Use class attributes to hold data or resources that persist during the class instance's life.
You can store things like database connections or counters as attributes. For example: class CounterDependency: def __init__(self): self.count = 0 def __call__(self): self.count += 1 return self.count Each time FastAPI calls this, it increases the count.
Result
The count increases with each request, showing state is preserved in the class instance.
Understanding how class instances keep state helps manage resources or track usage across requests.
4
IntermediateUsing __init__ for setup logic
🤔Before reading on: Will __init__ run once per request or once per app startup? Commit to your answer.
Concept: The __init__ method runs when FastAPI creates the dependency instance, useful for setup like opening connections.
For example: class DBSession: def __init__(self): self.connection = open_db_connection() def __call__(self): return self.connection FastAPI creates a new instance per request by default, so __init__ runs each time.
Result
Setup code runs before each request, ensuring fresh resources if needed.
Knowing when __init__ runs helps avoid expensive or repeated setup and manage resource lifetimes.
5
IntermediateDependency injection with class instances
🤔
Concept: Learn how to pass parameters to class dependencies and use them in endpoints.
You can create class instances with parameters and use them as dependencies: class Greeter: def __init__(self, name: str): self.name = name def __call__(self): return f'Hello {self.name}' @app.get('/') async def greet(message: str = Depends(Greeter('Alice'))): return {'msg': message}
Result
The endpoint returns 'Hello Alice' using the class dependency with a parameter.
Passing parameters to class dependencies allows flexible, reusable components.
6
AdvancedAsync class-based dependencies
🤔Before reading on: Can __call__ be async in class dependencies? Commit to your answer.
Concept: FastAPI supports async __call__ methods in class dependencies for asynchronous operations.
Define __call__ with async def to perform async tasks like database queries: class AsyncDependency: async def __call__(self): await asyncio.sleep(0.1) return 'Async result' Use it normally with Depends().
Result
FastAPI awaits the async __call__ and injects the result into the endpoint.
Supporting async __call__ lets class dependencies handle real-world async operations cleanly.
7
ExpertSingleton and scoped lifetimes in class dependencies
🤔Before reading on: Does FastAPI create a new class instance per request or reuse one? Commit to your answer.
Concept: By default, FastAPI creates a new instance per request, but you can control lifetime to reuse instances (singleton) or scope them differently.
You can create a single instance and pass it to Depends to share it across requests: singleton_dep = MyDependency() @app.get('/') async def root(dep: str = Depends(lambda: singleton_dep())): return {'msg': dep} Or use context managers and yield for scoped lifetimes.
Result
The same instance is reused, preserving state across requests if desired.
Controlling instance lifetime is crucial for resource management and performance in production.
Under the Hood
FastAPI uses Python's dependency injection system by inspecting function parameters with Depends. When a class instance is passed, FastAPI calls its __call__ method like a function. It creates a new instance of the class for each request by default, calling __init__ then __call__. Async __call__ methods are awaited. This mechanism allows FastAPI to inject dependencies automatically and manage their lifetimes.
Why designed this way?
Using classes with __call__ leverages Python's callable objects, making dependencies more flexible and organized. It avoids forcing users to write only functions and supports stateful dependencies naturally. This design fits Python's dynamic nature and keeps FastAPI lightweight without a complex DI container.
┌───────────────┐
│ FastAPI Start │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Create Class  │
│ Instance      │
│ (__init__)    │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Call __call__ │
│ (sync/async)  │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Inject Result │
│ into Endpoint │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does FastAPI reuse the same class instance for all requests by default? Commit to yes or no.
Common Belief:FastAPI creates one class instance and reuses it for all requests automatically.
Tap to reveal reality
Reality:FastAPI creates a new class instance for each request by default, calling __init__ every time.
Why it matters:Assuming reuse can cause bugs with shared mutable state or resource leaks if you expect fresh instances.
Quick: Can class dependencies only be synchronous? Commit to yes or no.
Common Belief:Class-based dependencies must have synchronous __call__ methods.
Tap to reveal reality
Reality:FastAPI supports async __call__ methods in class dependencies, allowing asynchronous operations.
Why it matters:Not knowing this limits your ability to handle async tasks like database calls efficiently.
Quick: Does passing parameters to class dependencies happen automatically? Commit to yes or no.
Common Belief:FastAPI automatically injects parameters into class __init__ methods like function dependencies.
Tap to reveal reality
Reality:FastAPI does not inject parameters into __init__; you must create the instance yourself with parameters before passing to Depends.
Why it matters:Expecting automatic injection causes confusion and errors when parameters are missing or wrong.
Quick: Is the __call__ method required for class dependencies? Commit to yes or no.
Common Belief:You can use any method name in a class for FastAPI dependencies.
Tap to reveal reality
Reality:FastAPI requires the class to be callable, so __call__ must be defined to use the class as a dependency.
Why it matters:Missing __call__ leads to runtime errors and confusion about how dependencies work.
Expert Zone
1
Class-based dependencies can implement context managers with async generators to manage resource lifetimes precisely.
2
Using class inheritance allows building complex dependency hierarchies and reusing shared logic elegantly.
3
FastAPI's dependency caching can be controlled with class dependencies by careful design of instance creation and scoping.
When NOT to use
Avoid class-based dependencies when your logic is simple and stateless; function dependencies are clearer and lighter. For very complex dependency graphs, consider external DI frameworks. Also, do not use class dependencies if you need automatic parameter injection into __init__, as FastAPI does not support this.
Production Patterns
In production, class-based dependencies often manage database sessions, API clients, or configuration objects. They are combined with context managers to open and close resources cleanly per request. Singleton instances are used for expensive-to-create objects shared across requests, improving performance.
Connections
Dependency Injection (general software design)
Class-based dependencies are a specific implementation of dependency injection in FastAPI.
Understanding class-based dependencies deepens knowledge of dependency injection principles, showing how to manage dependencies with state and lifecycle.
Python Callable Objects
Class-based dependencies rely on Python's ability to make class instances callable via __call__.
Knowing Python callable objects explains why classes can replace functions as dependencies, enabling more flexible designs.
Resource Management in Operating Systems
Managing resource lifetimes in class dependencies parallels OS concepts of opening, using, and closing resources safely.
Seeing class dependencies as resource managers helps understand why setup and teardown logic is critical and how to avoid leaks.
Common Pitfalls
#1Sharing mutable state unintentionally across requests.
Wrong approach:class Counter: count = 0 def __call__(self): self.count += 1 return self.count @app.get('/') async def root(counter: int = Depends(Counter())): return {'count': counter}
Correct approach:class Counter: def __init__(self): self.count = 0 def __call__(self): self.count += 1 return self.count @app.get('/') async def root(counter: int = Depends(Counter())): return {'count': counter}
Root cause:Using a class attribute instead of an instance attribute causes state to be shared globally, leading to unexpected behavior.
#2Expecting FastAPI to inject parameters into __init__ automatically.
Wrong approach:class Greeter: def __init__(self, name: str): self.name = name def __call__(self): return f'Hello {self.name}' @app.get('/') async def greet(greeting: str = Depends(Greeter)): return {'msg': greeting}
Correct approach:greeter_instance = Greeter('Alice') @app.get('/') async def greet(greeting: str = Depends(greeter_instance)): return {'msg': greeting}
Root cause:FastAPI does not call __init__ with parameters; you must create the instance yourself before passing to Depends.
#3Defining __call__ as a regular method but forgetting to make the class instance callable.
Wrong approach:class MyDep: def call(self): return 'Hi' @app.get('/') async def root(dep: str = Depends(MyDep())): return {'msg': dep}
Correct approach:class MyDep: def __call__(self): return 'Hi' @app.get('/') async def root(dep: str = Depends(MyDep())): return {'msg': dep}
Root cause:FastAPI requires the class instance to be callable, which means __call__ must be defined, not a method with a different name.
Key Takeaways
Class-based dependencies let you organize shared logic and state in FastAPI by using classes with a __call__ method.
FastAPI creates a new instance of the class per request by default, calling __init__ then __call__, supporting both sync and async methods.
You must create class instances with parameters yourself before passing them to Depends; FastAPI does not inject __init__ parameters automatically.
Managing resource lifetimes and state in class dependencies helps write cleaner, more maintainable, and testable API code.
Understanding Python callable objects and FastAPI's dependency injection mechanism is essential to use class-based dependencies effectively.