Pydantic Settings
Menggantikan os.getenv() yang tersebar dengan satu objek konfigurasi yang divalidasi sekali saat aplikasi start.
Intisari
- Satu class
Settingsmenggantikan semua panggilanos.getenv()di seluruh kodebase. - Field tanpa nilai default berarti wajib โ aplikasi gagal start kalau env var-nya kosong.
- Baca otomatis dari environment dan file
.env, dengan konversi tipe ("8000"โ8000). - Pakai
SecretStruntuk API key supaya tidak bocor ke log. - Gagal saat start jauh lebih baik daripada gagal di request pertama pengguna.
Masalah yang diselesaikan
# Tersebar di berbagai file
api_key = os.getenv("ANTHROPIC_API_KEY") # None kalau lupa diatur
max_tokens = int(os.getenv("MAX_TOKENS", "4096")) # crash kalau isinya bukan angka
debug = os.getenv("DEBUG") == "true" # "True" dan "1" tidak terdeteksi
Tiga masalah sekaligus: tidak tervalidasi, konversi tipe manual, dan kegagalan baru muncul saat runtime.
uv add pydantic-settings
from pydantic import Field, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
# Wajib โ tidak ada default, jadi aplikasi gagal start kalau kosong
anthropic_api_key: SecretStr
# Punya default
model: str = "claude-opus-5"
max_tokens: int = Field(default=4096, ge=1, le=128_000)
aws_region: str = "us-east-1"
debug: bool = False
allowed_origins: list[str] = ["http://localhost:3000"]
settings = Settings() # dibaca & divalidasi SEKALI, saat modul diimpor
Poin utamanya: kalau ANTHROPIC_API_KEY tidak diatur, aplikasi gagal
start dengan pesan yang jelas โ bukan berjalan normal lalu error 500 di request pertama
pengguna. Ini perbedaan antara masalah yang ketahuan saat deploy dan masalah yang ketahuan dari
laporan pengguna.
Dari mana nilainya diambil
Urutan prioritas, dari yang paling menang:
- Argumen saat membuat objek:
Settings(debug=True) - Environment variable
- File
.env - File secret (
/run/secrets/...โ untuk Docker) - Nilai default di class
Nama field dipetakan ke env var secara case-insensitive:
anthropic_api_key โ ANTHROPIC_API_KEY
max_tokens โ MAX_TOKENS
aws_region โ AWS_REGION
Contoh .env
ANTHROPIC_API_KEY=sk-ant-xxxxx
MAX_TOKENS=8192
DEBUG=true
ALLOWED_ORIGINS=["https://app.contoh.com","https://admin.contoh.com"]
Masukkan .env ke .gitignore. Commit .env.example
yang berisi nama-nama variabel dengan nilai kosong atau contoh โ supaya orang lain tahu apa yang
perlu diisi tanpa kamu membocorkan kredensial.
Prefix
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="APP_")
nama: str # โ dibaca dari APP_NAMA
port: int = 8000 # โ dibaca dari APP_PORT
Berguna supaya konfigurasi aplikasimu tidak bertabrakan dengan env var sistem.
Konfigurasi bersarang
from pydantic import BaseModel
class DatabaseSettings(BaseModel):
host: str = "localhost"
port: int = 5432
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_nested_delimiter="__")
db: DatabaseSettings = DatabaseSettings()
DB__HOST=prod.db.internal
DB__PORT=5433
SecretStr
settings.anthropic_api_key # SecretStr('**********')
print(settings) # key TIDAK muncul
logger.info("config: %s", settings) # aman
settings.anthropic_api_key.get_secret_value() # ambil nilai asli, eksplisit
Nilai SecretStr adalah pertahanan berlapis. Kebocoran API key ke log paling
sering terjadi karena seseorang mencetak seluruh objek konfigurasi saat debugging.
Dengan SecretStr, itu tidak mungkin terjadi tanpa sengaja.
Pola pemakaian
Objek modul (paling sederhana)
# src/myapp/settings.py
settings = Settings()
# di file lain
from myapp.settings import settings
client = anthropic.Anthropic(api_key=settings.anthropic_api_key.get_secret_value())
Fungsi ter-cache (lebih ramah testing)
from functools import lru_cache
@lru_cache
def get_settings() -> Settings:
return Settings()
Dengan bentuk ini, test bisa memanggil get_settings.cache_clear() lalu menyuntikkan konfigurasi berbeda.
Bersama FastAPI (Fase 5)
from typing import Annotated
from fastapi import Depends
@app.get("/config")
def baca_config(s: Annotated[Settings, Depends(get_settings)]):
return {"model": s.model, "max_tokens": s.max_tokens}
Validasi tambahan
from typing import Literal, Self
from pydantic import model_validator
class Settings(BaseSettings):
lingkungan: Literal["dev", "staging", "prod"] = "dev"
debug: bool = False
@model_validator(mode="after")
def debug_dilarang_di_prod(self) -> Self:
if self.lingkungan == "prod" and self.debug:
raise ValueError("debug tidak boleh aktif di production")
return self
Di dalam Docker
services:
api:
image: myapp
environment:
- MAX_TOKENS=8192
- AWS_REGION=ap-southeast-3
secrets:
- anthropic_api_key
secrets:
anthropic_api_key:
file: ./secrets/anthropic_api_key
class Settings(BaseSettings):
model_config = SettingsConfigDict(secrets_dir="/run/secrets")
anthropic_api_key: SecretStr
Pydantic Settings membaca file di secrets_dir yang namanya cocok dengan nama field.
Ini cara yang benar untuk menangani rahasia di container โ nilainya tidak pernah muncul di
docker inspect atau daftar environment variable.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.