FastAPI Development: Building a Simple REST API with Dependency Injection
FastAPI is one of the most modern and fast frameworks for building APIs in Python. It is built on top of Starlette and Pydantic, enabling developers to create high-performance APIs with a minimal amount of code. One of FastAPI’s standout features is its automatic validation of request data using Pydantic models. This article will guide you through creating a simple REST API with FastAPI, focusing on dependency injection, which allows for more flexible and maintainable code.
What is Dependency Injection in FastAPI?
Dependency Injection (DI) is a design pattern that allows you to inject dependencies into your functions or classes. In FastAPI, DI is used to handle the initialization of resources such as databases, services, or configurations, making it easier to manage and test different components of your application. FastAPI uses Python’s type hints to automatically inject the required dependencies into the endpoints of your application.
Setting Up Your FastAPI Project
Before we start building our API, let’s set up the project environment. You’ll need Python 3.7+ and the following dependencies:
fastapi– The FastAPI framework itself.uvicorn– An ASGI server to run your FastAPI app.pydantic– A library to handle data validation and parsing.
To install the dependencies, create a virtual environment and use pip:
python -m venv env source env/bin/activate # For macOS/Linux env\Scripts\activate # For Windows pip install fastapi uvicorn pydantic
Creating a Simple FastAPI Application
Let’s start by building a basic FastAPI application with a few endpoints. Our API will allow users to create, read, and delete a list of items.
from fastapi import FastAPI, Depends from pydantic import BaseModel from typing import List app = FastAPI() # Define a Pydantic model for the Item class Item(BaseModel): name: str description: str = None # Dummy database fake_db = [] # Dependency to simulate a database session def get_db(): return fake_db @app.post("/items/", response_model=Item) async def create_item(item: Item, db: List[Item] = Depends(get_db)): db.append(item) return item @app.get("/items/", response_model=List[Item]) async def get_items(db: List[Item] = Depends(get_db)): return db @app.delete("/items/{item_id}", response_model=Item) async def delete_item(item_id: int, db: List[Item] = Depends(get_db)): item = db.pop(item_id) return item
Breaking Down the Code
Let’s break down the key parts of the code:
Itemis a Pydantic model that defines the structure of the data we are working with. It requires aname(a string) and optionally adescription(also a string).fake_dbsimulates a database. In a real-world application, you would replace this with a real database (e.g., PostgreSQL or MongoDB).get_dbis a dependency function that returns the database (in this case, thefake_dblist). This function is injected into the endpoint functions using FastAPI’sDependsdependency injection system.- The
create_itemendpoint receives anItemmodel as the request body and appends it to the database (the fake list). It returns the item that was created. - The
get_itemsendpoint retrieves and returns all items from the database. - The
delete_itemendpoint allows us to delete an item by its ID from the database.
Running the Application
Now that we have the application set up, we can run it using Uvicorn. In the terminal, run:
uvicorn main:app --reload
This command tells Uvicorn to run the FastAPI app, and the --reload flag enables automatic reloading during development, so changes to the code will take effect without restarting the server.
Once the server is running, navigate to http://localhost:8000/docs to view the automatically generated API documentation powered by Swagger. Here, you can test the endpoints interactively, making it easier to try out your API without writing additional code for testing.
Adding More Complex Dependencies
In real-world applications, dependencies might not be as simple as just a list in memory. For example, you may need to interact with a database or an external service. Let’s add an example of a database connection using an external dependency.
from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker # SQLAlchemy database setup DATABASE_URL = "sqlite:///./test.db" engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) # Dependency to get the database session def get_db(): db = SessionLocal() try: yield db finally: db.close()
In the code above:
- We use SQLAlchemy to create a database engine and session for interacting with an SQLite database. In this example, we are using SQLite, but you could easily switch to PostgreSQL or MySQL by changing the
DATABASE_URL. - The
get_dbdependency now creates a new database session and yields it. The session will be automatically closed once the request is complete.
You can inject this database session dependency into any of your endpoint functions, just like we did with the simple fake_db dependency earlier.
Testing with Dependency Injection
One of the major benefits of dependency injection is the ease of testing. FastAPI allows us to replace dependencies during testing. For example, let’s test the create_item endpoint by using a mock database.
from fastapi.testclient import TestClient # Use the TestClient to make requests to our app client = TestClient(app) # Replace the database dependency with a mock version for testing def get_db_mock(): return [{"name": "Item 1", "description": "Test Item"}] # Inject the mock database for testing app.dependency_overrides[get_db] = get_db_mock def test_create_item(): response = client.post("/items/", json={"name": "New Item", "description": "A new test item"}) assert response.status_code == 200 assert response.json() == {"name": "New Item", "description": "A new test item"}
Here, we replace the get_db dependency with a mock version that always returns a predefined list of items. This allows us to test the API without worrying about the actual database state.
Conclusion
FastAPI’s dependency injection system is a powerful tool for structuring your applications and managing resources like databases, external services, and configurations. By leveraging dependencies, you can keep your code modular, reusable, and easy to test.
We’ve covered the basics of setting up a FastAPI project, building a simple REST API, and utilizing dependency injection. You can extend this example by connecting it to a real database, adding more complex business logic, and creating more advanced dependencies. The power of FastAPI and its dependency injection system will help you scale your projects efficiently.
Leave a Reply