1

I have the following scenario where I have a class representing a client to interact with an external service (such as Google Storage or another DB).

class SomeClient:
    pass

Now, I have multiple endpoints that need to utilize this client. I'm wondering what would be considered a better approach in terms of reusability and performance: whether to create the client myself once and use it in each endpoint, or alternatively, use the dependency injection mechanism.

Creating it on my own:

from fastapi import APIRouter

router = APIRouter()
client = SomeClient()

@router.get("/")
def hello():
    client.do_something()
    return "hello"

Versus using dependency injection:

from fastapi import APIRouter, Depends

router = APIRouter()

@router.get("/")
def hello(client: SomeClient = Depends()):
    client.do_something()
    return "hello"

What are the pros and cons of each method? The first approach makes more sense for me in terms of performance (no need to create an object on each request) so I wonder if, and when, use the dependency injection mechanism.

Thanks.

Omri
  • 43
  • 1
  • 4
  • You might find [this answer](https://stackoverflow.com/a/76322910/17865804) and [this answer](https://stackoverflow.com/a/73736138/17865804) helpful – Chris Aug 11 '23 at 04:21
  • Unless creating the client is expensive, you're probably not going to notice the difference. Injecting it as a dependency usually gives a clearer view of where everything in a controller is coming from - and if creating the object is expensive, you can wrap creating in a `get_some_client` function and add an `lru_cache` decorator to it to cache creation. I'd go with readability in this case, unless this is somewhere you've identified a performance issue. – MatsLindh Aug 11 '23 at 06:33
  • @Chris nice, thank you. I liked the lifespan option, a bit resembles what I'd do if I'd to code in in Java Quarkus. – Omri Aug 11 '23 at 10:04
  • @MatsLindh Thank you! I see what you say about readability when using injection, I'll take into consideration the `lru_cache` I wasn't aware of. – Omri Aug 11 '23 at 10:07
  • 1
    @MatsLindh Could you please name a few pros (if any) of using `lru_cache` over a `lifespan`/`asynccontextmanager`? Iinitialising the `client` in a `lifespan` event, in the way this is demonstrated in the links above, might be the proper approach; especially, if one needs closing/terminating the `client` on shutdown. – Chris Aug 11 '23 at 11:16
  • 1
    Either will probably work just fine - `lru_cache` will let you cache different dependencies based on a parameter to the dependency function, like what is usually done when creating security role definitions (and makes them easier to override). If, like as it seems in this case, there is generally no overhead to creating the object at all, I'm not sure the added complexity of either is necessary; also, you can easily add the lru_cache after the fact if you've used function as dependencies without having to change code around it now being attached to `state`and the lifespan. – MatsLindh Aug 11 '23 at 12:05

0 Answers0