Dependency injection is a beautiful concept. It is not limited to FastAPI. It is quite popular in statically typed languages such as Java. FastAPI embraces this concept and it is at the core of FastAPI.
The first question is: What is dependency injection?
It is a pattern in which an object receives other objects that it depends on. It is the responsibility of FastAPI to provide the needed dependencies.
The benefits of this approach are:
The best way to understand DI would be to see a working example. Before that, you will need to do a pip install fastapi uvicorn pytest requests in your virtual env.
Consider the below code:
#filename main.py
from fastapi import FastAPI, HTTPException, status, Depends
development_db = ["DB for Development"]
def get_db_session():
return development_db
app = FastAPI()
@app.get("/add-item/")
def add_item(item:str, db = Depends(get_db_session)):
db.append(item)
print(db)
return {"message":f"added item {item}"}
This is a very simple code that tries to set up a database session and add items to the database. I have tried to keep it simple so that we can focus on the DI concept. I am using a list to simulate a database such as Postgres, MySQL etc.
We need to start the development server with uvicorn main:app --reload on the terminal. Now, if we hit the URL endpoint and send a query parameter e.g. http://127.0.0.1:8000/add-item/?item=salt then we should see the below lines in the terminal. The first item of the list is "DB for Development", This means our development database is in use.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
['DB for Development', 'salt']
You must be knowing about unit testing. In industry, unit tests are a kind of a must. We have used a list named development_db for the development server. However, we don't want to clutter the development database with redundant rows created during unit tests. We want a way to use a completely different database for testing purposes.
It is very easy as we haven't hardcoded the database part in the path function. We are getting the database session as a dependency.
FastAPI provides an elegant way to override the dependencies. Let's create a test_dependency_injection.py file and write the below code.
from fastapi.testclient import TestClient
from main import app,get_db_session
testing_db = ["DB for testing"]
def get_testing_db():
return testing_db
app.dependency_overrides[get_db_session] = get_testing_db
client = TestClient(app)
def test_item_should_add_to_database():
response = client.get(
"/add-item/?item=sugar",
)
assert response.status_code == 200
assert response.text == '{"message":"added item sugar"}'
Concentrate on the line: app.dependency_overrides[get_db_session] = get_testing_db We could elegantly override the database setup part and provide another database named testing_db. Now if you type pytest -s in the terminal. You should see
(env) C:\v2\basics\dependency_injection> pytest -s
collected 1 item
test_db_overides.py ['DB for testing', 'sugar']
.
================================================== 1 passed in 0.49s ==
The -s is important to see the output of the print statement in the terminal. Notice that this time, the first item on the list is 'DB for testing' which means we successfully overrode the use of our development database. Isn't it just beautiful! We will use the concept of dependency injection a lot. So, I will cover even more cases of DI. For now over and out!
Brige the gap between Tutorial hell and Industry. We want to bring in the culture of Clean Code, Test Driven Development.
We know, we might make it hard for you but definitely worth the efforts.
© Copyright 2022-23 Team FastAPITutorial