Saat membangun aplikasi Python, kamu pasti sering menerima data dari luar: respon API, input pengguna, file JSON, atau konfigurasi. Masalahnya, Python adalah bahasa dynamically typed — ia tidak akan menghentikan programmu ketika kamu memasukkan string "123" ke fungsi yang mengharapkan angka. Akibatnya, bug sering baru ketahuan setelah aplikasi berjalan dan data sudah menyebar ke seluruh sistem. Di sinilah Pydantic berperan: library ini memungkinkan kamu mendefinisikan struktur dan tipe data secara eksplisit, lalu memvalidasinya secara otomatis saat runtime. Dalam tutorial ini, kita akan membahas Pydantic v2 dari nol hingga mahir, lengkap dengan contoh kode yang bisa langsung kamu jalankan.
Catatan untuk pengguna Pydantic v1: Tutorial ini menggunakan Pydantic v2. Beberapa API berubah, misalnya @validator menjadi @field_validator, dan .dict()/.json() digantikan oleh model_dump()/model_dump_json(). Pastikan kamu menginstal versi 2.x agar semua contoh di bawah berjalan.
Apa Itu Pydantic dan Mengapa Kamu Membutuhkannya?
Pydantic adalah library validasi data untuk Python yang dibangun di atas fitur type hints. Alih-alih menulis puluhan baris kode if untuk mengecek tipe dan nilai data, kamu cukup mendefinisikan sebuah model dengan anotasi tipe, dan Pydantic akan menangani sisanya: memvalidasi tipe, mengonversi data, menampilkan pesan error yang jelas, serta menyediakan serialisasi ke JSON. Pydantic v2 (dirilis tahun 2023) ditulis ulang dengan engine Rust di balik layar, sehingga jauh lebih cepat — bahkan hingga 5–50× lebih cepat dari v1 untuk banyak kasus.
Kenapa Pydantic penting? Pydantic adalah fondasi banyak framework populer seperti FastAPI, LangChain, dan LlamaIndex. Dengan memahami Pydantic, kamu otomatis memahami cara kerja validasi data di belakang framework-framework tersebut.
Langkah 1 — Instalasi Pydantic v2
Pastikan kamu menggunakan Python 3.8 ke atas, lalu instal Pydantic melalui pip. Jika kamu menggunakan uv sebagai package manager, prosesnya juga sama mudahnya.
pip install "pydantic>=2"
Kamu bisa mengecek versi yang terpasang dengan perintah singkat berikut:
import pydantic
print(pydantic.VERSION)
# Output contoh: 2.7.0
Langkah 2 — Membuat Model Pertama dengan BaseModel
Inti dari Pydantic adalah class BaseModel. Kamu mendefinisikan model dengan mewarisi BaseModel dan menuliskan atribut beserta anotasi tipenya, persis seperti menulis dataclass. Mari kita buat model sederhana untuk data pengguna.
from pydantic import BaseModel
from datetime import datetime
class User(BaseModel):
id: int
name: str
email: str
is_active: bool = True
created_at: datetime
# Data mentah dari "dunia luar" (misalnya respon API)
data = {
"id": "123", # string -> otomatis dikonversi ke int
"name": "Budi Santoso",
"email": "budi@example.com",
"created_at": "2024-05-01T10:30:00",
}
user = User(**data)
print(user)
print(user.id, type(user.id)) # 123 <class 'int'>
print(user.created_at, type(user.created_at)) # objek datetime
Perhatikan hal menarik di atas: Pydantic mengonversi string "123" menjadi integer 123, dan string tanggal menjadi objek datetime secara otomatis. Proses ini disebut coercion. Jika datanya benar-benar tidak valid, Pydantic akan melempar ValidationError dengan pesan yang sangat informatif.
Langkah 3 — Menangkap Error Validasi
from pydantic import BaseModel, ValidationError
class User(BaseModel):
id: int
name: str
age: int
try:
User(id="bukan-angka", name="Budi", age="dua puluh")
except ValidationError as e:
print(e)
Error yang dihasilkan akan menampilkan lokasi field yang bermasalah, tipe yang diharapkan, dan nilai yang diterima — jauh lebih mudah dibaca daripada sekadar TypeError biasa. Ini sangat membantu saat melakukan debugging pada data yang datang dari API eksternal.
Langkah 4 — Menambah Constraint dengan Field
Selain tipe, kamu juga bisa menambahkan batasan (constraint) pada nilai, seperti panjang minimal, nilai maksimal, atau pola tertentu. Gunakan fungsi Field() untuk itu.
from pydantic import BaseModel, Field
class Product(BaseModel):
name: str = Field(min_length=3, max_length=50)
price: float = Field(gt=0) # harus lebih besar dari 0
stock: int = Field(ge=0, le=1000) # 0 <= stock <= 1000
tags: list[str] = []
print(Product(name="Laptop", price=12000000, stock=15))
Untuk validasi yang lebih spesifik, Pydantic juga menyediakan tipe khusus seperti EmailStr untuk alamat email dan AnyUrl untuk URL.
from pydantic import BaseModel, EmailStr, AnyUrl
class Contact(BaseModel):
email: EmailStr
website: AnyUrl | None = None
c = Contact(email="budi@example.com", website="https://bengkelkode.com")
print(c)
Tipe EmailStr dan AnyUrl memerlukan package tambahan email-validator. Instal sekaligus dengan pip install "pydantic[email]" agar validasi email berjalan akurat.
Langkah 5 — Validator Kustom dengan @field_validator
Kadang validasi bawaan tidak cukup. Misalnya, kamu ingin memastikan username ditulis dalam format tertentu atau password memenuhi aturan keamanan. Gunakan decorator @field_validator untuk menambah logika validasi sendiri.
from pydantic import BaseModel, field_validator
class Account(BaseModel):
username: str
password: str
@field_validator("username")
@classmethod
def normalize_username(cls, v: str) -> str:
v = v.strip().lower()
if " " in v:
raise ValueError("username tidak boleh mengandung spasi")
return v
@field_validator("password")
@classmethod
def password_strength(cls, v: str) -> str:
if len(v) < 8:
raise ValueError("password minimal 8 karakter")
return v
user = Account(username="Budi", password="rahasia123")
print(user.username) # 'budi'
Langkah 6 — Model Bertingkat (Nested Models)
Data di dunia nyata jarang berbentuk datar. Sering kali kamu punya objek di dalam objek. Pydantic menangani ini dengan sangat alami: cukup gunakan model lain sebagai tipe field. Kamu bahkan bisa langsung mem-parsing JSON menjadi objek Python dengan model_validate_json().
from pydantic import BaseModel
class Address(BaseModel):
street: str
city: str
zip_code: str
class Customer(BaseModel):
name: str
address: Address
orders: list[dict] = []
payload = '''
{
"name": "Siti Aminah",
"address": {"street": "Jl. Merdeka No. 10", "city": "Bandung", "zip_code": "40111"},
"orders": [{"id": 1, "total": 250000}]
}
'''
customer = Customer.model_validate_json(payload)
print(customer.address.city) # Bandung
print(customer.model_dump_json(indent=2))
model_dump() dan model_dump_json() adalah cara standar untuk mengubah model kembali menjadi dict atau string JSON — berguna saat kamu mengirim data ke database atau API. Jika kamu bekerja dengan database relasional, pola ini mirip dengan cara SQLAlchemy ORM memetakan objek Python ke tabel database.
Langkah 7 — Mengelola Konfigurasi dengan pydantic-settings
Pydantic juga punya modul khusus untuk membaca konfigurasi aplikasi dari environment variable atau file .env. Ini sangat berguna untuk menyimpan kredensial database, API key, dan pengaturan deployment tanpa menuliskannya langsung di kode.
pip install pydantic-settings
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
app_name: str = "BengkelKode App"
debug: bool = False
database_url: str
model_config = SettingsConfigDict(env_file=".env")
settings = Settings() # otomatis membaca .env dan environment variable
print(settings.database_url)
Dengan pendekatan ini, nilai DATABASE_URL yang tersimpan di file .env atau environment variable sistem akan otomatis dimuat dan divalidasi. Ini menghindari praktik buruk hard-coding kredensial langsung di source code.
Langkah 8 — Integrasi dengan FastAPI
Salah satu alasan terbesar untuk mempelajari Pydantic adalah integrasinya yang mulus dengan FastAPI. Di FastAPI, model Pydantic langsung dipakai untuk mendefinisikan request body dan response, sehingga validasi serta dokumentasi API dibuat otomatis.
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
price: float = Field(gt=0)
quantity: int = Field(ge=1, default=1)
@app.post("/items/")
def create_item(item: Item):
# item sudah tervalidasi di sini
return {"message": f"{item.name} diterima", "total": item.price * item.quantity}
Begitu request masuk dengan data yang tidak valid (misalnya price bernilai negatif), FastAPI otomatis mengembalikan respon 422 lengkap dengan detail error dari Pydantic — tanpa kamu menulis satu baris pun logika validasi tambahan.
Kesimpulan
Pydantic adalah salah satu library yang paling berdampak dalam ekosistem Python modern. Dengan menambahkan type hints dan model BaseModel, kamu mendapatkan validasi data otomatis, pesan error yang jelas, serialisasi JSON, dan integrasi yang rapi dengan framework seperti FastAPI. Mulailah dari model sederhana, lalu bertahap gunakan Field, validator kustom, dan pydantic-settings. Selamat mencoba, dan jangan ragu untuk bereksperimen dengan contoh-contoh di atas!
Apakah Pydantic cocok untuk proyek kecil?
Ya, bahkan untuk skrip kecil pun Pydantic membantu menangkap bug data lebih awal. Overhead-nya sangat ringan, dan di Pydantic v2 performanya sudah sangat cepat berkat engine Rust. Kamu tidak perlu memakainya di setiap variabel — cukup di titik-titik masuk data (boundary), seperti parsing API atau file konfigurasi.
Apa bedanya Pydantic dengan dataclass bawaan Python?
Dataclass hanya menyimpan data tanpa validasi runtime. Pydantic melakukan validasi tipe, konversi otomatis (coercion), serialisasi JSON, dan menghasilkan pesan error yang informatif. Jika kamu hanya butuh struktur data sederhana, dataclass sudah cukup; jika kamu butuh validasi input, pilih Pydantic.