Dependency Injection (DI) is a design pattern that allows us to write more flexible, modular, and testable code. In the context of FastAPI, it's a powerful tool that can significantly improve the structure and maintainability of your applications.
But what exactly is dependency injection? At its core, it's a technique where the dependencies of a class or function are "injected" from the outside, rather than being created within the class or function itself. This inversion of control leads to looser coupling between components and greater flexibility in your codebase.
FastAPI's dependency injection system offers several advantages:
Let's dive into how we can implement dependency injection in FastAPI.
FastAPI makes it easy to use dependency injection. Here's a simple example:
from fastapi import FastAPI, Depends app = FastAPI() def get_db(): return "DB Connection" @app.get("/items") async def read_items(db: str = Depends(get_db)): return {"db": db}
In this example, get_db
is a dependency that's injected into the read_items
endpoint. FastAPI will automatically call get_db
and pass its result to read_items
.
You can also use classes as dependencies. This is particularly useful for more complex dependencies:
from fastapi import FastAPI, Depends app = FastAPI() class DatabaseConnection: def __init__(self): self.db = "DB Connection" def get_items(self): return ["item1", "item2", "item3"] def get_db(): return DatabaseConnection() @app.get("/items") async def read_items(db: DatabaseConnection = Depends(get_db)): return {"items": db.get_items()}
Here, we're injecting an instance of DatabaseConnection
into our endpoint.
FastAPI also supports sub-dependencies, where one dependency depends on another:
from fastapi import FastAPI, Depends app = FastAPI() def get_token_header(token: str = Depends(get_token)): if token != "secret-token": raise HTTPException(status_code=400, detail="Invalid token") return token def get_query_token(token: str = ""): return token @app.get("/items") async def read_items(token: str = Depends(get_token_header)): return {"token": token}
In this example, get_token_header
depends on get_token
.
For resources that need cleanup, FastAPI provides yield dependencies:
from fastapi import FastAPI, Depends app = FastAPI() async def get_db(): db = await connect_to_db() try: yield db finally: await db.close() @app.get("/items") async def read_items(db = Depends(get_db)): return await db.fetch_all("SELECT * FROM items")
This ensures that the database connection is properly closed after the endpoint is processed.
FastAPI allows you to override dependencies, which is incredibly useful for testing:
from fastapi import FastAPI, Depends from fastapi.testclient import TestClient app = FastAPI() def get_db(): return "Production DB" @app.get("/db") async def read_db(db: str = Depends(get_db)): return {"db": db} client = TestClient(app) def override_get_db(): return "Test DB" app.dependency_overrides[get_db] = override_get_db def test_read_db(): response = client.get("/db") assert response.status_code == 200 assert response.json() == {"db": "Test DB"}
This allows you to replace the real database with a test one during your unit tests.
By effectively using dependency injection in FastAPI, you'll create more modular, testable, and maintainable applications. It might take a bit of getting used to, but the benefits are well worth the effort!
25/09/2024 | Python
14/11/2024 | Python
26/10/2024 | Python
17/11/2024 | Python
06/12/2024 | Python
17/11/2024 | Python
22/11/2024 | Python
15/11/2024 | Python
05/11/2024 | Python
26/10/2024 | Python
05/10/2024 | Python
14/11/2024 | Python