FastAPI + Dependency Injector





Hi,



I have released a new version of Dependency Injector 4.4. It allows you to use Dependency Injector with FastAPI . In this post I will show you how it works.



The main task of the integration is to make friends with the DependsFastAPI directive with markers Provideand the ProviderDependency Injector.



This did not work out of the box prior to DI 4.4. FastAPI uses typing and Pydantic for input and response validation. Dependency Injector markers puzzled him.



The solution came after examining the internals of FastAPI. I had to make several changes in the wiring module of the Dependency Injector. The directive Dependsnow works with markers Provideand Provider.



Example



Create a file fastapi_di_example.pyand put the following lines in it:



import sys

from fastapi import FastAPI, Depends
from dependency_injector import containers, providers
from dependency_injector.wiring import inject, Provide


class Service:
    async def process(self) -> str:
        return 'Ok'


class Container(containers.DeclarativeContainer):

    service = providers.Factory(Service)


app = FastAPI()


@app.api_route('/')
@inject
async def index(service: Service = Depends(Provide[Container.service])):
    result = await service.process()
    return {'result': result}


container = Container()
container.wire(modules=[sys.modules[__name__]])


To run the example, install the dependencies:



pip install fastapi dependency-injector uvicorn


and run uvicorn:



uvicorn fastapi_di_example:app --reload


The terminal should display something like:



INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [11910] using watchgod
INFO:     Started server process [11912]
INFO:     Waiting for application startup.
INFO:     Application startup complete.


but http://127.0.0.1:8000should return:



{
    "result": "Ok"
}




How to test?



Create a file next tests.pyto it and put the following lines in it:



from unittest import mock

import pytest
from httpx import AsyncClient

from fastapi_di_example import app, container, Service


@pytest.fixture
def client(event_loop):
    client = AsyncClient(app=app, base_url='http://test')
    yield client
    event_loop.run_until_complete(client.aclose())


@pytest.mark.asyncio
async def test_index(client):
    service_mock = mock.AsyncMock(spec=Service)
    service_mock.process.return_value = 'Foo'

    with container.service.override(service_mock):
        response = await client.get('/')

    assert response.status_code == 200
    assert response.json() == {'result': 'Foo'}


To run the tests, install the dependencies:



pip install pytest pytest-asyncio httpx


and run pytest:



pytest tests.py


The terminal should display:



======= test session starts =======
platform darwin -- Python 3.8.3, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: ...
plugins: asyncio-0.14.0
collected 1 item

tests.py .                                                      [100%]

======= 1 passed in 0.17s =======


What does integration give?



FastAPI is a cool API building framework. The basic dependency injection mechanism is built into it.



This integration improves the FastAPI dependency injection experience. It allows you to use providers, overrides, config and resources of the Dependency Injector in it.



What's next?






All Articles