Dependencies
Maya's developers are getting tired. The same pagination parameters appear in every endpoint. The same check for a missing menu item is copied again and again. And every order lookup opens a database connection, that must be closed afterwards. Copy and paste is how bugs are born. FastAPI has a clean answer. Dependency injection.

What is a dependency?
A dependency is just a function. You add it to an endpoint with Depends, and FastAPI calls it for you, and passes in the result. You describe what an endpoint needs, and FastAPI provides it. That is the whole idea.
def pagination(skip: int = 0, limit: int = 2):
return {"skip": skip, "limit": limit}
@app.get("/menu/")
def list_menu(page: dict = Depends(pagination)):
...A reusable pagination dependency
Here is a dependency for pagination. It takes skip and limit as query parameters, and returns them in a dictionary. With Annotated, we give it a short name. Now any endpoint can ask for pagination in one line, and the docs show those parameters automatically.
def pagination(skip: int = 0, limit: int = 2):
return {"skip": skip, "limit": limit}
PageDep = Annotated[dict, Depends(pagination)]
@app.get("/menu/")
def list_menu(page: PageDep):
...One dependency, two endpoints
Both the menu and the orders endpoint use it. Skip and limit work in both places, and we wrote the logic only once.
curl -s 'localhost:8000/menu/?limit=2' [{"name":"Chocolate Cake","price":18.0},{"name":"Sourdough Loaf","price":7.0}] curl -s 'localhost:8000/orders/?skip=1&limit=1' {"db_connected":true,"orders":[{"id":1002,"item":"Sourdough Loaf"}]}
A dependency that checks for you
Dependencies can raise errors, too. This one looks up a menu item, and raises a four hundred and four, if it is missing. Endpoints that need an item simply ask for it, and never repeat the check. Item three is found. Item ninety nine returns our error.
def get_item_or_404(item_id: int):
item = MENU.get(item_id)
if item is None:
raise HTTPException(status_code=404,
detail="Menu item not found")
return item
@app.get("/menu/{item_id}")
def read_item(item: ItemDep):
return itemcurl -s localhost:8000/menu/3 {"name":"Croissant","price":4.0} curl -s localhost:8000/menu/99 {"detail":"Menu item not found"}
yield: set up and clean up
A dependency with yield can also clean up. Our get db function opens a connection, and yields it to the endpoint. The code after yield runs when the request ends. Look at the server log. Opening, then the request, then closing, for every call that uses it.
def get_db():
print("opening database connection")
db = {"connected": True}
try:
yield db
finally:
print("closing database connection")fastapi dev main.py INFO: 127.0.0.1:52605 - "GET /menu/?limit=2 HTTP/1.1" 200 OK opening database connection INFO: 127.0.0.1:52607 - "GET /orders/?skip=1&limit=1 HTTP/1.1" 200 OK closing database connection INFO: 127.0.0.1:52609 - "GET /menu/3 HTTP/1.1" 200 OK INFO: 127.0.0.1:52611 - "GET /menu/99 HTTP/1.1" 404 Not Found
The complete code for this lesson
Here is the full main.py exactly as it runs at the end of this lesson. Save it, install FastAPI with pip install "fastapi[standard]", and start it with fastapi dev main.py, then open http://127.0.0.1:8000/docs.
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
app = FastAPI(title="Sweet Crumbs API")
MENU = {
1: {"name": "Chocolate Cake", "price": 18.0},
2: {"name": "Sourdough Loaf", "price": 7.0},
3: {"name": "Croissant", "price": 4.0},
4: {"name": "Flat White", "price": 5.0},
}
ORDERS = [
{"id": 1001, "item": "Chocolate Cake"},
{"id": 1002, "item": "Sourdough Loaf"},
{"id": 1003, "item": "Croissant"},
]
def pagination(skip: int = 0, limit: int = 2):
return {"skip": skip, "limit": limit}
PageDep = Annotated[dict, Depends(pagination)]
def get_item_or_404(item_id: int):
item = MENU.get(item_id)
if item is None:
raise HTTPException(status_code=404, detail="Menu item not found")
return item
ItemDep = Annotated[dict, Depends(get_item_or_404)]
def get_db():
print("opening database connection")
db = {"connected": True}
try:
yield db
finally:
print("closing database connection")
DbDep = Annotated[dict, Depends(get_db)]
@app.get("/menu/")
def list_menu(page: PageDep):
items = list(MENU.values())
return items[page["skip"] : page["skip"] + page["limit"]]
@app.get("/menu/{item_id}")
def read_item(item: ItemDep):
return item
@app.get("/orders/")
def list_orders(page: PageDep, db: DbDep):
start = page["skip"]
return {"db_connected": db["connected"], "orders": ORDERS[start : start + page["limit"]]}Summary
Dependencies keep your code dry, easy to test, and easy to change. Soon we will use them for the database session, and for login. And login is next. Maya needs to protect the staff only endpoints. In the next lesson, authentication. See you there.
Key takeaways- A dependency is just a function
- Depends() injects the result
- yield sets up and cleans up