Menguasai dependency injection FastAPI: Depends, sub-dependencies dan chain, class dependencies, dependensi global lewat router dan app, pola Annotated modern, serta dependensi ber-yield untuk manajemen resource.

Setelah di episode 7 kita mengendalikan output dengan response_model, sekarang kita membahas fitur yang paling membedakan FastAPI dari framework lain: dependency injection (DI). Di episode 2 kita sudah berkenalan dengan Depends sederhana — sekarang kita buka semua kekuatannya.
Mengapa episode ini penting? Karena DI adalah jawaban FastAPI atas pertanyaan arsitektur yang sering membuat kode berantakan: di mana menaruh logika autentikasi, koneksi database, pagination, atau current user? Jawaban yang buruk: salin-tempel di tiap endpoint. Jawaban FastAPI: dependency — ditulis sekali, dipakai di mana pun, dan bisa diganti saat testing.
Dependency adalah fungsi biasa yang hasilnya di-inject ke handler. FastAPI mengelola siklusnya:
from typing import Annotated
from fastapi import Depends, Query
def pagination_params(
skip: int = Query(0, ge=0),
limit: int = Query(10, ge=1, le=100),
) -> dict[str, int]:
return {"skip": skip, "limit": limit}
PaginationDep = Annotated[dict[str, int], Depends(pagination_params)]Dengan Annotated, deklarasi dependency jadi satu alias yang bisa dipakai di banyak endpoint:
from typing import Annotated
from fastapi import FastAPI, Depends
from app.deps import PaginationDep
app = FastAPI()
Pagination = Annotated[dict[str, int], Depends(pagination_params)]
@app.get("/items/")
def list_items(pagination: Pagination) -> dict:
return paginationPerhatikan keuntungan pola Annotated:
dict), metadata tersimpan di Depends.Pagination dipakai berulang tanpa menulis ulang tanda tangan panjang.Important
Ini gaya yang direkomendasikan dokumentasi FastAPI 0.141: deklarasikan dependency sebagai Annotated[Type, Depends(...)], bukan param: Type = Depends(...). Kode lebih bersih, dan perilaku konsisten saat memakai subclass atau reuse.
Dependency bisa memanggil dependency lain — FastAPI mengeksekusi seluruh rantai secara otomatis:
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
def get_prefix() -> str:
return "item"
def get_full_prefix(prefix: Annotated[str, Depends(get_prefix)]) -> str:
return f"{prefix}-v2"
@app.get("/version/")
def version(full_prefix: Annotated[str, Depends(get_full_prefix)]) -> dict:
return {"prefix": full_prefix}FastAPI membangun DAG dependensi: ia mendeteksi get_full_prefix membutuhkan get_prefix, menjalankan get_prefix dulu, lalu menginject hasilnya. Untuk memakai ini, kita pip install python-dotenv... eh, tidak — murni fitur bawaan, tanpa package tambahan.
Dependency tidak harus fungsi — class dengan __init__ juga didukung, cocok untuk resource yang perlu inisialisasi:
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
class Counter:
def __init__(self) -> None:
self.count = 0
def hit(self) -> int:
self.count += 1
return self.count
@app.get("/count/")
def count(counter: Annotated[Counter, Depends()]) -> dict[str, int]:
return {"count": counter.hit()}Depends() tanpa argumen memakai class itu sendiri sebagai dependency — FastAPI menginstansiasi Counter dan menginjectnya. Catatan penting: satu instance per request. Dependency FastAPI tidak punya cache antar-request (kecuali di-scope khusus), jadi jangan mengandalkan state antar request.
Agar logika tertentu berjalan di semua endpoint, pasang dependency di level router atau app. Ini cara ideal untuk menyisipkan verifikasi API key, logging, atau rate limiting secara global:
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
async def verify_api_key(x_api_key: str = Header(...)) -> None:
if x_api_key != "secret-key":
raise HTTPException(status_code=401, detail="API key salah")
app = FastAPI(dependencies=[Depends(verify_api_key)])
@app.get("/protected/")
def protected() -> dict[str, str]:
return {"message": "terlindungi"}dependencies=[...] di konstruktor FastAPI membuat verify_api_key dieksekusi sebelum setiap request — dan tetap muncul sebagai "Security" di dokumentasi OpenAPI. Kita juga bisa menambahkan ke router tertentu (bukan semua app) — ini dibahas saat kita memecah aplikasi di episode 9.
yield mengubah dependency menjadi context manager: kode sebelum yield jalan sebelum request, kode setelahnya jalan setelah response dikirim. Ini pola utama untuk koneksi database dan sesi:
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
def get_db_session():
session = "contoh-session"
try:
yield session
finally:
session = None # tutup resource di sini
@app.get("/db/")
def read(session: Annotated[str, Depends(get_db_session)]) -> dict:
return {"session": session}finally menjamin pembersihan bahkan saat handler melempar error. Di episode 9, pola ini menjadi rumah bagi sesi SQLAlchemy — dan di episode 10, dependency override-nya menjadi senjata utama testing.
Warning
Dependency dengan yield tidak boleh menangkap HTTPException sebelum yield dan tetap perlu menangani cleanup-nya sendiri. FastAPI akan menyelesaikan dependency generator setelah response selesai — manfaatkan finally untuk menutup koneksi, bukan menaruh cleanup di luar try.
| Pola | Sintaks | Kapan dipakai |
|---|---|---|
| Fungsi | def fn() -> T + Depends(fn) | Logika kecil, query params |
| Annotated alias | X = Annotated[T, Depends(fn)] | Reuse banyak endpoint |
| Sub-dependency | Depends(other) dalam dependency | Logika bertingkat |
| Class | Depends() tanpa argumen | Resource terinisialisasi |
| Global | FastAPI(dependencies=[...]) | Seluruh app / router |
| yield | yield + finally | Manajemen resource (DB) |
Inti yang harus dibawa pulang:
Depends menginject hasil fungsi/class ke handler; FastAPI menyusun rantainya otomatis.Annotated adalah gaya modern yang direkomendasikan FastAPI 0.141.dependencies=[...]) untuk logika lintas app/router.yield untuk resource lifecycle — fondasi sesi database di episode 9.Di episode 9 selanjutnya kita akan membangun CRUD lengkap dengan SQLAlchemy — menggabungkan semua yang kita pelajari: Pydantic models untuk schema, dependency untuk sesi database, SQLAlchemy 2.x sebagai ORM, dan Alembic untuk migrasi. Ini episode yang paling banyak ditunggu, karena di sinilah aplikasi FastAPI mulai "hidup" dengan data persisten!