Beranda / Kursus / FastAPI for Beginners / III — Best Practices
III — Best Practices
Artikel 6 dari 6

Best Practices & Next Steps

Kesalahan umum pemula FastAPI, cara menulis test efektif dengan pytest dan `TestClient`, checklist debug ketika error, dan roadmap lanjutan setelah menguasai dasar-dasarnya.

10 Juli 2026

Selamat, kamu sudah punya API yang jalan end-to-end — dari endpoint sederhana sampai CRUD lengkap dengan validasi dan database sungguhan. Di artikel penutup ini, kita bahas hal-hal yang sering bikin pemula terjebak, cara menulis test yang efektif, dan ke mana kamu bisa lanjut belajar setelah ini.

Kesalahan Umum Pemula

  • Menandai fungsi async def tapi memanggil kode blocking di dalamnya. Ini yang paling sering bikin performa aplikasi FastAPI jadi lebih lambat, bukan lebih cepat. Kalau di dalam fungsi kita ada panggilan library sinkron (misalnya driver database yang tidak mendukung async, atau time.sleep()), itu akan memblokir seluruh event loop — bukan cuma request itu, tapi SEMUA request lain yang sedang ditangani server. Kalau ragu, pakai def biasa; FastAPI otomatis menjalankannya di thread pool terpisah.
  • Tidak memakai response_model. Tanpa response_model, gampang tidak sengaja mengembalikan field internal yang seharusnya tidak pernah keluar ke publik (password hash, catatan internal, dan sejenisnya). Selalu definisikan skema response secara eksplisit, seperti TaskRead yang kita pakai di Bagian 4.
  • Business logic ditulis langsung di dalam path operation function. Untuk aplikasi kecil ini masih wajar, tapi begitu aplikasi tumbuh, endpoint yang berisi query database, validasi bisnis, DAN pemanggilan API eksternal sekaligus jadi sulit di-test dan dibaca. Pisahkan logic yang lebih kompleks ke fungsi/module tersendiri (biasa disebut service layer), biarkan path operation function fokus menerima request dan mengembalikan response.
  • Hardcode konfigurasi seperti connection string atau secret key. Sama seperti prinsip di seri CI/CD for Beginners — jangan pernah commit credential ke kode. Untuk FastAPI, pola umumnya pakai pydantic-settings untuk membaca konfigurasi dari environment variable dengan validasi tipe yang sama seperti model biasa.
  • Tidak menangani error spesifik, cuma mengandalkan 500 Internal Server Error generik. Kalau ada kemungkinan error yang bisa diprediksi (data tidak ditemukan, konflik, tidak punya izin), tangani eksplisit dengan HTTPException dan status code yang tepat — jangan biarkan traceback mentah bocor ke konsumen API.
  • Tidak menulis test sama sekali. Karena FastAPI memvalidasi banyak hal otomatis, gampang merasa "toh sudah divalidasi Pydantic, ngapain test lagi". Padahal validasi otomatis cuma menjamin bentuk data benar — bukan menjamin logic aplikasi kita benar. Business logic (seperti perhitungan, aturan otorisasi, interaksi antar data) tetap butuh test eksplisit.
  • Testing dengan Pytest dan TestClient

    FastAPI menyediakan TestClient yang memungkinkan kita memanggil endpoint tanpa benar-benar menjalankan server — cocok dipakai bersama pytest. Untuk aplikasi yang terhubung ke database, pola standarnya adalah override dependency get_session dengan database in-memory khusus testing, supaya test tidak menyentuh tasks.db sungguhan:

    import pytest
    from fastapi.testclient import TestClient
    from sqlmodel import Session, SQLModel, create_engine
    from sqlmodel.pool import StaticPool
    
    from app.database import get_session
    from app.main import app
    
    
    @pytest.fixture(name="session")
    def session_fixture():
        engine = create_engine(
            "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool
        )
        SQLModel.metadata.create_all(engine)
        with Session(engine) as session:
            yield session
    
    
    @pytest.fixture(name="client")
    def client_fixture(session: Session):
        def get_session_override():
            return session
    
        app.dependency_overrides[get_session] = get_session_override
        with TestClient(app) as client:
            yield client
        app.dependency_overrides.clear()
    
    
    def test_create_task(client: TestClient):
        response = client.post("/api/tasks", json={"title": "Belajar FastAPI"})
        assert response.status_code == 201
        assert response.json()["title"] == "Belajar FastAPI"
    

    Poin kuncinya: app.dependency_overrides[get_session] = get_session_override mengganti dependency asli dengan versi test, tanpa mengubah satu baris pun kode endpoint. Inilah manfaat konkret dependency injection yang dibahas di Bagian 5 — desain yang memisahkan "cara mendapatkan resource" dari "logic endpoint" membuat kode jadi mudah diuji secara terisolasi.

    Jalankan dengan:

    pip install -r requirements.txt
    pytest -v
    

    Checklist Debug Ketika Error

    Kalau API kamu mengembalikan error dan bingung mulai dari mana, coba urutan ini:

  • Baca detail error di /docs, bukan cuma status code-nya. Response 422 biasanya sudah menyertakan field mana yang gagal validasi dan kenapa — jangan langsung menebak.
  • Bedakan error validasi (422) dari error business logic (404, 400, dst). Kalau errornya 422, masalahnya di bentuk data yang dikirim vs skema Pydantic. Kalau 404/400/dst yang kita lempar manual, cek logic di path operation function-nya.
  • Reproduce lewat /docs atau curl dulu, sebelum curiga ke kode frontend/klien. Ini mengisolasi apakah masalahnya di API atau di sisi pemanggil.
  • Untuk error yang cuma muncul di beberapa request (bukan semua), curigai kondisi data — misalnya request yang menyasar id yang memang tidak ada, bukan bug di endpoint-nya.
  • Untuk error terkait database, cek urutan session.add()session.commit()session.refresh(). Lupa commit() adalah penyebab umum data "kelihatan tersimpan" di kode tapi tidak benar-benar masuk database.
  • Merapikan Konfigurasi dengan pydantic-settings

    Sebagai langkah lanjutan dari yang dibahas di best practice #4, pydantic-settings memberi pola yang konsisten untuk membaca environment variable dengan validasi tipe:

    from pydantic_settings import BaseSettings
    
    
    class Settings(BaseSettings):
        database_url: str = "sqlite:///./tasks.db"
        debug: bool = False
    
    
    settings = Settings()
    

    Nilai-nilai ini otomatis dibaca dari environment variable (DATABASE_URL, DEBUG) atau file .env, dengan validasi tipe yang sama seperti model Pydantic biasa — kalau DEBUG diisi nilai yang bukan boolean, aplikasi gagal start dengan pesan error yang jelas, bukan bug tersembunyi yang baru ketahuan belakangan.

    Roadmap Lanjutan — Mau Belajar Apa Lagi?

    Seri ini sengaja fokus ke dasar-dasar biar kamu punya fondasi kuat. Kalau mau lanjut lebih dalam, ini beberapa arah yang bisa dieksplorasi:

  • Autentikasi & otorisasi — FastAPI punya dukungan bawaan untuk OAuth2 dan JWT lewat fastapi.security, plus dependency injection yang sama persis untuk melindungi endpoint tertentu (Depends(get_current_user)).
  • Async database driver — untuk memanfaatkan penuh performa async, ganti driver SQLite/PostgreSQL sinkron dengan versi async (misalnya asyncpg untuk PostgreSQL), dan ubah endpoint jadi async def sungguhan.
  • Database migration dengan Alembic — begitu skema Task berubah di production (nambah kolom, dst.), butuh cara terkontrol untuk mengubah struktur tabel tanpa kehilangan data. Alembic adalah tool standar untuk ini di ekosistem SQLAlchemy/SQLModel.
  • Background tasks & job antrian — untuk pekerjaan yang tidak perlu selesai sebelum response dikirim (kirim email, proses file besar), FastAPI punya BackgroundTasks bawaan untuk kasus sederhana, dan bisa naik ke Celery/RQ untuk kasus yang lebih berat.
  • Containerization dengan Docker — mengemas aplikasi FastAPI (beserta dependency-nya) jadi image yang konsisten dari lokal sampai production.
  • Menyambungkan ke pipeline CI/CD — begitu API-mu punya test suite yang solid (seperti yang baru kita bangun), langkah alami berikutnya adalah mengotomasi test dan deployment-nya. Kalau belum pernah, seri CI/CD for Beginners membahas persis ini dari nol — trigger, job, matrix build, sampai deploy otomatis — memakai studi kasus yang konsepnya sama persis dengan apa yang baru kamu bangun di sini.
  • Kabar baiknya, karena kamu sudah paham konsep dasarnya — path operation, Pydantic model, dependency injection — mempelajari fitur FastAPI lain nantinya tinggal soal memperdalam pola yang sama, bukan belajar dari nol lagi.

    Penutup

    Kita sudah menempuh perjalanan dari "kenapa FastAPI penting", memahami anatomi aplikasi, praktik membuat endpoint pertama, memperketat validasi data, sampai menyambungkan ke database sungguhan lengkap dengan dependency injection. Yang paling penting, kamu sekarang punya template API nyata yang bisa dipakai ulang dan dikembangkan untuk proyekmu sendiri.

    Terima kasih sudah mengikuti seri FastAPI for Beginners ini. Selamat mencoba di proyek kamu sendiri!

    Topik

    FastAPIBest Practices
    III — Best Practices · Article 6 of 6