icon

fastapipythondomain-driven-architecturedeployment

Learn FastAPI with me

Mon Jul 06 2026

Lama sudah tidak membuat materi pembelajaran, kali ini aku datang dengan membawa materi daging yang bener-bener daging. Anjai. Oke kali ini aku akan membahas dan memberikan suatu sedikit tutorial tentang backend, yaitu FastAPI.

Artikel ini ditujukan untuk pemula yang ingin mulai belajar backend menggunakan Python maupun developer yang ingin mencoba framework baru selain Django atau Flask.

Table of Contents

Kenapa FastAPI?

Sebelum mulai, mari kita kenalan dulu dengan FastAPI.

FastAPI merupakan framework backend modern yang dibangun di atas Starlette untuk web framework dan Pydantic untuk validasi data. Kombinasi tersebut membuat FastAPI menjadi sangat cepat sekaligus nyaman digunakan.

Beberapa alasan mengapa banyak developer memilih FastAPI:

  • Performa tinggi karena berjalan di atas ASGI. ASGI adalah (Asynchronous Server Gateway Interface) yaitu standar komunikasi antara aplikasi Python dan web server yang dirancang untuk mendukung pemrosesan asynchronous. ASGI merupakan penerus dari WSGI yang hanya mendukung aplikasi sinkron (synchronous).
  • Dokumentasi API otomatis.
  • Validasi request menggunakan type hint Python.
  • Mudah dipelajari.
  • Sangat cocok untuk membangun REST API maupun microservices.
  • Mendukung asynchronous programming (async/await) secara native.

Kalau kamu sudah familiar dengan Python, belajar FastAPI akan terasa mudah.

Domain Driven Architecture

Pada materi kali ini saya menggunakan pendekatan DDD (Domain-Driven Design) atau yang sering dikenal sebagai Domain Driven Architecture. Seperti apa sih arsitektur ini?

Simpelnya gini, ketika kita pertama kali membuat aplikasi, biasanya struktur project masih sederhana. Contoh.

fastapi-apps/
├── models/
├── schemas/
├── services/
├── routers/
├── database.py
└── main.py

Struktur tersebut sudah cukup baik untuk aplikasi sederhana. Namun, ketika aplikasi mulai berkembang—misalnya aplikasi e-commerce yang memiliki fitur products, categories, cart, orders, payments, hingga authentication—setiap fitur akan memiliki model, schema, service, dan router masing-masing.

Akibatnya, ketika ingin mengembangkan satu fitur, misalnya Order, kita harus berpindah-pindah folder sehingga project menjadi semakin sulit dinavigasi.

Dengan DDD, setiap fitur dikelompokkan ke dalam satu folder sehingga seluruh kode yang berkaitan dengan fitur tersebut berada di tempat yang sama.

fastapi-apps/
├── domains/
   ├── auth/
   ├── products/
   ├── categories/
   ├── orders/
   └── payments/
├── core/
├── config/
└── main.py

Melalui pendekatan ini, struktur project menjadi lebih rapi, mudah dipahami, dan lebih mudah dikembangkan seiring bertambahnya kompleksitas aplikasi. Seluruh materi pada seri ini juga akan menggunakan struktur tersebut agar sejak awal kita terbiasa membangun project yang terorganisir.

Setup Proyek FastAPI dengan Domain Driven Architecture

Struktur folder

app/
├── modules/
   └── users/
       ├── user_router.py
       ├── user_service.py
       ├── user_models.py
       └── user_schemas.py

├── db/
   └── conn.py
├── .env
├── __init__.py
└── main.py

Buat folder kosong

mkdir fastapi-apps # (ini bebas mau kalian beri nama apa)

Setup python virtual environment

python3 -m venv .venv

Lalu masuk ke virtual environment

source .venv/bin/activate

Install Dependency yang dibutuhkan

pip install "fastapi[standard]" SQLAlchemy cuid2 "psycopg[binary]"

Kita menggunakan SQLAlchemy untuk database engine, dan psycopg untuk koneksi ke postgresql (kita menggunakan postgresql untuk database)

Buat file .env untuk menyimpan environment kita

touch .env

Lalu isikan

DATABASE_URL="postgresql+psycopg://[user]:[password]@localhost:5432/[db-name]"

Eksekusi Proyek

Pada bagian ini kita mulai mengeksekusi proyeknya, step pertama yaitu konfigurasi koneksi db

Step 1: Setup DB Connection

Pada file conn.py kita bisa taruh kode berikut

import os
import dotenv

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

dotenv.load_dotenv()

# URL
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql+psycopg://[user]:[password]@localhost:5432/[db-name]")

# engine
engine = create_engine(DATABASE_URL)
# session maker
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# base for models
Base = declarative_base()

# dependency
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

Koneksi ini digunakan sebagai konfigurasi utama database pada aplikasi. Nantinya seluruh module yang membutuhkan akses database akan menggunakan konfigurasi ini.

  • create_engine() digunakan untuk membuat koneksi ke database berdasarkan DATABASE_URL.
  • SessionLocal digunakan untuk membuat session yang akan dipakai saat menjalankan operasi database seperti SELECT, INSERT, UPDATE, dan DELETE.
  • Base menjadi parent class untuk seluruh model SQLAlchemy yang akan kita buat.
  • get_db() merupakan dependency FastAPI yang bertugas membuat session di awal request dan menutupnya secara otomatis setelah request selesai diproses.

Dengan konfigurasi ini, kita tidak perlu lagi membuat atau menutup koneksi database secara manual pada setiap endpoint.

Step 2: Membuat Schema dan Model users

Apa itu schema dan Model? Schema digunakan untuk memvalidasi data request dan membentuk response API, sedangkan Model adalah representasi struktur tabel di database

  1. Buat file user_schema.py di folder modules/users/

    from datetime import datetime
    from pydantic import BaseModel, EmailStr
    
    class UserBase(BaseModel):
      name: str
      username: str
      email: EmailStr
    
    
    class UserCreate(UserBase):
      password: str
    
    class UserUpdate(BaseModel):
        name: str | None = None
        email: EmailStr | None = None
        password: str | None = None
    
    class UserResponse(UserBase):
        id: str
        created_at: datetime
        updated_at: datetime
    
        # allow population by attribute name (e.g. from ORM objects)
        model_config = {
            "from_attributes": True
        }

    Pada Schema, kita mendefinisikan struktur data yang diterima dan dikembalikan oleh API.

    • UserCreate digunakan saat membuat user baru.
    • UserUpdate digunakan saat mengubah data user.
    • UserResponse digunakan sebagai format response yang dikirim ke client.
  2. Buat file user_model.py di folder modules/users/

    from sqlalchemy import Column, String, DateTime
    from sqlalchemy.sql import func
    from cuid2 import Cuid
    
    from app.db.conn import Base
    
    # model
    class User(Base):
      __tablename__ = "users"
    
      id = Column(String, primary_key=True, default=lambda: Cuid().generate())
    
      name = Column(String, nullable=False)
      username = Column(String, unique=True, nullable=False)
      email = Column(String, unique=True, nullable=False)
      password = Column(String, nullable=False)
    
      created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
      updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)

    Sementara itu, Model mendefinisikan struktur tabel users pada database menggunakan SQLAlchemy. Field seperti id, created_at, dan updated_at akan dikelola secara otomatis, sedangkan username dan email diberi constraint unique agar tidak ada data yang duplikat.

Step 3: Service dan Route user

Nah ini bagian paling krusial dari aplikasi ini, yaitu Service dan Route. Service berisi seluruh logika bisnis aplikasi, sedangkan Route bertugas menerima request dari client, memanggil service yang sesuai, lalu mengembalikan response.

  1. Buat file user_service.py di folder modules/users/

    from fastapi import HTTPException
    
    # ambil session dari sqlalchemy ORM
    from sqlalchemy.orm import Session
    # import model User
    from app.modules.users.user_model import User
    # import model Schema
    from app.modules.users.user_schema import UserCreate, UserUpdate
    
    # create
    def create_user(user: UserCreate, db: Session):
      payload = User(**user.model_dump())
    
      db.add(payload)
      db.commit()
      db.refresh(payload)
    
      return payload
    
    # find all
    def find_all_user(db: Session):
      users = db.query(User).all()
    
      if not users:
        raise HTTPException(status_code=404, detail="Users not found")
    
      return users
    
    # find one
    def find_one_user(id: str, db: Session):
      user = db.query(User).filter(User.id == id).first()
    
      if not user:
        raise HTTPException(status_code=404, detail="User not found")
    
      return user
    
    # update
    def update_user(id: str, user: UserUpdate, db: Session):
      payload = find_one_user(id, db)
    
      if not payload:
        raise HTTPException(status_code=404, detail="User not found")
    
      update_data = user.model_dump(exclude_unset=True)
      for key, value in update_data.items():
        setattr(payload, key, value)
    
      db.commit()
      db.refresh(payload)
    
      return payload
    
    # delete
    def delete_user(id: str, db: Session):
      payload = find_one_user(id, db)
    
      if not payload:
        raise HTTPException(status_code=404, detail="User not found")
    
      db.delete(payload)
      db.commit()
  2. Buat file user_route.py di folder modules/users/

    from fastapi import APIRouter, Depends, status
    
    from sqlalchemy.orm import Session
    from app.db.conn import get_db
    
    # import schema
    from app.modules.users.user_schema import UserCreate, UserUpdate, UserResponse
    # import service
    from app.modules.users.user_service import create_user, find_all_user, find_one_user, update_user, delete_user
    
    # inisialisasi prefix untuk users
    router = APIRouter(prefix="/users", tags=["users"])
    
    # create
    @router.post("/", response_model=UserResponse)
    async def create(user: UserCreate, db: Session = Depends(get_db)):
      return create_user(user, db)
    
    # find all
    @router.get("/", response_model=list[UserResponse])
    async def find_all(db: Session = Depends(get_db)):
      return find_all_user(db)
    
    # find one
    @router.get("/{id}", response_model=UserResponse)
    async def find_one(id: str, db: Session = Depends(get_db)):
      return find_one_user(id, db)
    
    # update
    @router.patch("/{id}", response_model=UserResponse)
    async def update(id: str, user: UserUpdate, db: Session = Depends(get_db)):
      return update_user(id, user, db)
    
    # delete
    @router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT)
    async def delete(id: str, db: Session = Depends(get_db)):
      delete_user(id, db)
    
      return status.HTTP_204_NO_CONTENT

Step 4: Entry point

Setelah semua sudah selesai, kita akan membuat entry point dari apps ini. Kita buat file main.py dan isikan ini

import time
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app.db.conn import engine, SessionLocal

# users
from app.modules.users.user_route import router as user_router
from app.modules.users.user_model import User

app = FastAPI()

# Allow CORS for all origins, methods, and headers
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

# create tables
User.metadata.create_all(bind=engine)

@app.get("/")
async def health_check():
    return {"message": f"Health checked at {int(time.time() * 1000)} ms", "status": 200}

app.include_router(user_router)

@app.on_event("startup")
async def startup_event():
    db = SessionLocal()

    print("App started at", int(time.time() * 1000), "ms")

    db.close()

Step 5: Testing API

Pada tahap ini kita uji API yang sudah kita buat, kita bisa menggunakan postman, REST Client, tapi FastAPI sendiri sudah menyediakan UI untuk testing menggunakan swagger miliknya yang dapat kalian akses, pertama jalankan dulu aplikasinya

fastapi dev

Lalu buka URL http://localhost:8000/docs untuk membuka swagger UI dan langsung dapat kalian test untuk API tersebut.

Kesimpulan

Sampai tahap ini kita telah berhasil membangun sebuah REST API sederhana menggunakan FastAPI dengan struktur Domain Driven Architecture (DDD). Mulai dari menyiapkan project, mengonfigurasi koneksi database, membuat Schema dan Model, hingga mengimplementasikan Service dan Route untuk operasi CRUD.

Tentu saja aplikasi ini masih sangat sederhana. Masih banyak hal yang perlu ditambahkan agar siap digunakan pada lingkungan production, seperti autentikasi, otorisasi, validasi yang lebih kompleks, logging, testing, migrasi database, hingga deployment.

Namun, fondasi yang kita bangun pada artikel ini sudah cukup baik sebagai titik awal untuk mengembangkan aplikasi FastAPI yang lebih besar dan terstruktur.

Pada artikel selanjutnya kita akan membahas beberapa topik lanjutan, seperti:

  • Hashing password menggunakan bcrypt.
  • Authentication menggunakan JWT.
  • Database migration dengan Alembic.
  • Pagination, filtering, dan searching.
  • Upload file.
  • Deployment menggunakan Docker dan Nginx.

Semoga artikel ini bermanfaat dan sampai jumpa di seri Learn FastAPI with Me berikutnya. 🚀

Disclaimer

Agar artikel ini tetap fokus pada dasar-dasar FastAPI, ada beberapa hal yang sengaja tidak dibahas secara mendalam. Salah satunya adalah konfigurasi CORS (Cross-Origin Resource Sharing). Jika backend akan diakses oleh aplikasi frontend (misalnya React, Vue, Next.js, atau Flutter Web), maka kamu perlu menambahkan middleware CORS agar browser mengizinkan komunikasi antara frontend dan backend. Konfigurasi tersebut akan dibahas pada artikel tersendiri bersamaan dengan pembahasan deployment dan keamanan aplikasi.

Related Posts

No related posts

Built with Astro by Whodareuu © 2026 - CC BY-NC-SA 4.0

CC BY NC SA