7. Dependency Injection in FastAPI

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:

  • Separation of Concern: We can focus on the business logic instead of instantiating additional generic objects. For e.g. if there is an endpoint that takes in the contact us form data and sends an email. We can separate the part of setting up an email client.
  • Shared Logic: We can extract the shared logic. A common piece of code can be declared once and can be referenced again and again. For e.g. In APIs, it's very common to hit the database and fetch information or update the information.  For these tasks, we need a database session. If we write the code to get a database session for each endpoint then it will clutter our codebase. Instead, we can extract this part and just reference it at each endpoint.
  • Testing Made Easy: FastAPI makes it very easy to override the dependencies during testing. This helps us avoid unnecessary patching of the code.

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!

FastAPITutorial

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.

Contacts

Refunds:

Refund Policy
Social

Follow us on our social media channels to stay updated.

© Copyright 2022-23 Team FastAPITutorial