Ooze integration with FastAPI

Overview

FastAPI has its own dependency injection system that does not directly integrate with Ooze. That being said, Ooze provides a special @magic_dependable decorator that can be used to bridge to FastAPI’s dependency injection system.

A typical use of FastAPI’s dependency injection system would look like the following:

 1from fastapi import Depends, FastAPI
 2
 3app = FastAPI()
 4
 5async def dependable_query(q: Union[str, None]):
 6    return q
 7
 8@app.get('/')
 9async def get_root(search: str = Depends(dependable_query)):
10    return {"search_query": search}

One would think that they could simply decorate get_root with the @ooze.magic decorator to include Ooze dependencies in the call. Unfortunately, FastAPI does its own argument inspection on routed calls to get_root fails because FastAPI wouldn’t be able to find the dependencies in its own dependency graph.

 1from fastapi import Depends, FastAPI
 2import ooze
 3
 4app = FastAPI()
 5
 6async def dependable_query(q: Union[str, None]):
 7    return q
 8
 9@ooze.provide
10def upper_query(value: str) -> str:
11    return value.upper()
12
13@app.get('/')
14@ooze.magic    # !!!!! THIS FAILS - IT JUST WON'T WORK !!!!!
15async def get_root(search: str = Depends(dependable_query), upper_query):
16    return {"search_query": upper_query(search)}

To get around this, Ooze provides a @magic_dependable decorator that you can use on a function that accepts Ooze dependencies.

 1from fastapi import Depends, FastAPI
 2import ooze
 3
 4app = FastAPI()
 5
 6async def dependable_query(q: Union[str, None]):
 7    return q
 8
 9@ooze.provide
10def upper_query(value: str) -> str:
11    return value.upper()
12
13@ooze.magic_dependable
14def upper_factory(upper_query):  # Ooze injects upper_query
15    return upper_query
16
17@app.get('/')
18async def get_root(search: str = Depends(dependable_query), upper_query=Depends(upper_factory):
19    return {"search_query": upper_query(search)}

Anything that is function decorated with @ooze.magic_dependable can be referenced from FastAPI’s Depends. Once decorated with @ooze.magic_dependable, the function will get all its dependencies injected from Ooze.

FastAPI support both async def and def function definitions for Dependables. Ooze will honor both.

Since FastAPI dependencies are nothing more specific than Python callables you can just as easily decorate a class with @ooze.magic_dependable. Here’s another example:

 1import ooze
 2from fastapi import Depends, FastAPI
 3
 4app = FastAPI()
 5
 6ooze.provide_static('name', 'world')
 7
 8@ooze.provide
 9def upper(value: str) -> str:
10    return value.upper()
11
12@ooze.magic_dependable
13class Greeter:
14    def __init__(self, name: str, upper: callable):
15        self._name = name
16        self._upper = upper
17
18    @property
19    def greeting(self):
20        return f"Hello {self._upper(self._name)}"
21
22@app.get("/items")
23async def read_items(greeter: Greeter = Depends(Greeter)):
24    return {'greeting': greeter.greeting}

In both cases (function as a dependency or class as dependency), it’s important to note. All of the function arguments (or constructor arguments) need to be injectable by Ooze. The @ooze.magic_dependable decorator tricks FastAPI into thinking your dependency doesn’t take any arguments at all because Oooze will be providing them.