Di artikel sebelumnya, kita sudah paham kenapa FastAPI dibuat. Sekarang saatnya membedah apa saja yang membentuk sebuah aplikasi FastAPI. Ibaratnya, sebelum masak, kita kenalan dulu dengan bahan-bahan dapurnya — supaya begitu masuk ke praktik menulis endpoint pertama, istilah-istilah yang muncul tidak lagi bikin bingung.
Path Operation — Unit Dasar sebuah Endpoint
Path operation adalah istilah FastAPI untuk "satu endpoint API": kombinasi antara path (URL, misalnya /api/tasks) dan operation (HTTP method, misalnya GET atau POST). Di FastAPI, satu path operation didefinisikan dengan decorator:
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health_check():
return {"status": "ok"}
Beberapa hal yang perlu dipahami dari contoh di atas:
@app.get("/health") — decorator yang mendaftarkan fungsi di bawahnya sebagai path operation untuk method GET di path /health.health_check disebut path operation function — ini yang benar-benar dijalankan ketika ada request masuk.dict) otomatis diubah FastAPI menjadi response JSON. Tidak perlu json.dumps() manual.HTTP Method — Memetakan Operasi CRUD
FastAPI mendukung semua HTTP method standar lewat decorator yang senama: @app.get, @app.post, @app.put, @app.patch, @app.delete. Konvensinya mengikuti semantik REST:
| Method | Dipakai untuk | Contoh |
|---|---|---|
| GET | Mengambil data, tanpa efek samping | GET /api/tasks — ambil semua task |
| POST | Membuat data baru | POST /api/tasks — buat task baru |
| PUT | Mengganti seluruh data yang ada | PUT /api/tasks/1 — ganti semua field task #1 |
| PATCH | Mengubah sebagian data yang ada | PATCH /api/tasks/1 — ubah field tertentu saja |
| DELETE | Menghapus data | DELETE /api/tasks/1 — hapus task #1 |
Tiga Sumber Data: Path, Query, dan Body
Ini salah satu konsep paling penting untuk dipahami sebelum menulis endpoint apa pun, karena FastAPI membedakan ketiganya otomatis berdasarkan cara kita mendeklarasikan parameter di fungsi.
Path Parameter
Bagian dari URL itu sendiri, dideklarasikan dengan kurung kurawal di path dan sebagai argumen fungsi:
@app.get("/api/tasks/{task_id}")
def get_task(task_id: int):
...
task_id di sini WAJIB ada di setiap request — dia bagian dari alamat resource-nya. FastAPI otomatis mengonversinya ke int sesuai type hint, dan menolak dengan error validasi kalau yang dikirim bukan angka.
Query Parameter
Parameter yang ditulis di fungsi tapi TIDAK muncul di path, otomatis dianggap FastAPI sebagai query parameter — muncul di URL setelah ?, seperti /api/tasks?done=true:
@app.get("/api/tasks")
def list_tasks(done: bool | None = None):
...
Karena punya default value (None), parameter done ini opsional. Kalau tidak dikirim, nilainya None. Kalau dikirim ?done=true, FastAPI otomatis mengonversi string "true" jadi boolean True.
Request Body
Untuk data yang lebih kompleks (biasanya dikirim lewat POST/PUT/PATCH), kita pakai Pydantic model sebagai tipe parameter — dibahas di bagian berikutnya.
Aturan sederhananya: kalau namanya cocok dengan placeholder di path → path parameter. Kalau tipenya Pydantic model → body. Selain itu → query parameter. Tidak perlu decorator berbeda-beda seperti di beberapa framework lain; FastAPI menyimpulkannya dari signature fungsi.
Pydantic Model — Skema Data sebagai Kode Python Biasa
Untuk data yang berbentuk objek (misalnya "task baru" yang punya title dan priority), kita definisikan skemanya sebagai class yang mewarisi BaseModel dari Pydantic:
from pydantic import BaseModel
class TaskCreate(BaseModel):
title: str
priority: str = "medium"
Begitu model ini dipakai sebagai tipe parameter di path operation function:
@app.post("/api/tasks")
def create_task(payload: TaskCreate):
return {"title": payload.title, "priority": payload.priority}
FastAPI otomatis: membaca body request sebagai JSON, memvalidasi field-nya sesuai TaskCreate (tipe benar, field wajib ada), lalu memberikan objek TaskCreate yang sudah tervalidasi ke dalam fungsi kita. Kalau body yang dikirim tidak sesuai skema — misalnya title berupa angka atau tidak dikirim sama sekali — FastAPI otomatis merespons 422 Unprocessable Entity lengkap dengan detail field mana yang salah, tanpa kita menulis satu baris kode validasi pun.
Status Code — Eksplisit, Bukan Tebak-tebakan
Secara default, response sukses memakai status 200 OK. Tapi kita bisa (dan sebaiknya) menentukan status code yang lebih tepat secara eksplisit:
from fastapi import status
@app.post("/api/tasks", status_code=status.HTTP_201_CREATED)
def create_task(payload: TaskCreate):
...
Memakai konstanta dari fastapi.status (dibanding angka mentah seperti 201) membuat kode lebih mudah dibaca dan mengurangi typo. Ini konsep yang akan kita pakai terus di endpoint POST (201 Created) dan DELETE (204 No Content) pada sesi praktik nanti.
Dokumentasi Otomatis — /docs dan /redoc
Ini salah satu fitur paling terasa manfaatnya. Tanpa konfigurasi tambahan apa pun, setiap aplikasi FastAPI otomatis menyediakan dua halaman dokumentasi interaktif:
/docs — Swagger UI, tampilan interaktif tempat kita bisa melihat semua endpoint, skema request/response, dan bahkan mengirim request langsung dari browser untuk mencoba API-nya./redoc — ReDoc, tampilan dokumentasi yang lebih rapi untuk dibaca (read-only, tanpa fitur "try it out").Kedua halaman ini digenerate dari OpenAPI schema (/openapi.json) yang otomatis dibuat FastAPI berdasarkan path operation, Pydantic model, dan type hint yang kita tulis. Karena sumbernya sama persis dengan kode yang berjalan, dokumentasi ini tidak mungkin basi — begitu kita ubah kode, dokumentasinya otomatis ikut berubah saat aplikasi di-restart.
async def vs def — Kapan Pakai yang Mana?
FastAPI mendukung dua jenis path operation function: yang biasa (def) dan yang asynchronous (async def). Aturan sederhananya:
async def kalau di dalam fungsi kita memanggil operasi I/O yang juga mendukung async/await — misalnya query ke database dengan driver async, atau memanggil API eksternal dengan client HTTP async. Ini membuat FastAPI bisa menangani request lain selagi menunggu I/O tersebut selesai.def biasa kalau fungsi kita memanggil operasi yang sifatnya blocking (sinkron) — misalnya library database yang belum mendukung async. FastAPI otomatis menjalankan fungsi def biasa di thread pool terpisah, supaya tidak memblokir event loop utama.Yang perlu dihindari: menulis async def tapi di dalamnya memanggil kode blocking sinkron secara langsung (tanpa await). Ini justru bisa memblokir seluruh event loop dan membuat semua request lain menunggu — kebalikan dari tujuan async. Kita akan bahas ini lebih lanjut di artikel best practices di akhir seri.
Sekilas Dependency Injection
Satu konsep lagi yang akan sering muncul: Depends(). Ini cara FastAPI mengelola dependency — sesuatu yang dibutuhkan path operation function untuk berjalan, misalnya koneksi database. Untuk sekarang, cukup tahu bahwa ini ada dan tujuannya memisahkan "cara mendapatkan resource" dari "logic endpoint itu sendiri". Kita akan bahas ini secara mendalam saat masuk ke sesi praktik integrasi database.
Rangkuman
@app.get(...).?), request body (lewat Pydantic model)./docs, /redoc) digenerate langsung dari kode, selalu sinkron.async def untuk operasi I/O async, def biasa untuk operasi blocking/sinkron.