1

I define a service as a class that provides DRY methods that can be called from anywhere in the code.

My FastAPI controller has an endpoint:

@router.get("/health")
async def health_check():
  return {"status": "pass"}

I would like to update it to return MyHelper().get_health_status, where MyHelper is defined in ./services/my_helper.py. What is the standard approach here?

Ideally, I would like to use dependency injection like in .NET core, where the process is roughly:

  1. Define the service in a namespace (.NET doesn't care about file pathing as long as it's the same root).
  2. Import the namespace into the Startup.cs file.
  3. Register the service with the application in the aforementioned Startup file: ConfigureServices(IServiceCollection services) -> services.AddSingleton<IMyHelper, MyHelper>();
  4. Inject the helper interface into the constructor of any other files in the project.

What's the best way to approach this in a FastAPI project? Is there DI built in or what third party library should be used? If no DI is built in, how do I register my service so I can at least instantiate it inside router ("controller") files?

P.S. I am reading What is a Pythonic way for Dependency Injection?, but am still wondering if there is a preference when using FastAPI specifically. Again, given how REST APIs are basically built into .NET Core and there is a "standard" way (which is not just instantiating helper classes).

VSO
  • 11,546
  • 25
  • 99
  • 187
  • 1
    When I started with Python for APIs, also coming from a .NET background, I had a similar question as yours. I ended up going with singleton. In your class file, at the very end, create the instance, e.g.: `my_helper = MyHelper()`, then from anywhere in your project, you import `my_helper` and use it directly. – AndreFeijo Mar 28 '23 at 22:00
  • Looks like I may end up doing just that. – VSO Mar 29 '23 at 00:43

2 Answers2

1

When I started with Python for APIs, also coming from a .NET background, I had a similar question as yours. I ended up going with singleton. In your class file, at the very end, create the instance, e.g.:

my_helper = MyHelper()

Then from anywhere in your project, you import my_helper and use it directly.

It's arguable that Dependency Injection as we know in .NET is not the "Pythonic" way of doing things, but for FastAPI specifically there is a way to do it using Depends.

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
    return commons


@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
    return commons

More info can be found here: https://fastapi.tiangolo.com/tutorial/dependencies/

AndreFeijo
  • 10,044
  • 7
  • 34
  • 64
1

To expand on AndreFeijo's answer with an example of how you'd use MyHelper as a dependency in FastAPI:

Using FastAPI's Depends you can create the dependency when it's needed:

from services.my_helper import MyHelper


def get_my_helper():
    return MyHelper()


@router.get("/health")
async def health_check(my_helper: Annotated[MyHelper, Depends(get_my_helper)]):
  return {"status": my_helper.get_health_status()}

The Annotated syntax is only valid for FastAPI 0.95 and above.

In older versions of FastAPI you'll have to do:

async def health_check(my_helper: MyHelper = Depends(get_my_helper)):

.. instead.

MatsLindh
  • 49,529
  • 4
  • 53
  • 84