โ† Semua pembelajaran / Python untuk AI Engineer
Fase 3 ยท Async & HTTP

HTTPX

httpx menggantikan requests: API yang hampir identik, tapi mendukung async, HTTP/2, dan timeout yang benar.

Sumber asli python-httpx.org Resmi Rangkuman ~6 menit baca

Intisari

  • API-nya hampir sama dengan requests, jadi pengetahuanmu terbawa langsung.
  • Selalu pakai objek Client, bukan fungsi tingkat modul โ€” koneksi jadi dipakai ulang.
  • Timeout default httpx adalah 5 detik. Untuk panggilan LLM, ini terlalu pendek โ€” atur eksplisit.
  • raise_for_status() harus dipanggil sendiri; httpx tidak melempar otomatis pada 4xx/5xx.
  • Satu library, dua mode: Client untuk sinkron, AsyncClient untuk async.

Kenapa httpx, bukan requests

requestshttpx
Dukungan asyncโŒ Tidak adaโœ… AsyncClient
HTTP/2โŒโœ… (opsional)
Timeout defaultTidak ada โ€” menunggu selamanya5 detik
Type hintMinimLengkap
StreamingTerbatasSync & async
StatusPerawatan minimalAktif

Dasar

uv add httpx
import httpx

r = httpx.get("https://api.contoh.com/data")

r.status_code       # 200
r.json()            # parse JSON
r.text              # sebagai string
r.content           # sebagai bytes
r.headers["content-type"]
r.elapsed           # timedelta โ€” durasi request

Ini bentuk untuk mencoba di REPL saja. Setiap panggilan tingkat modul membuat koneksi TCP dan handshake TLS baru, lalu membuangnya. Untuk kode sungguhan, pakai Client.

Client โ€” cara yang benar

with httpx.Client(
    base_url="https://api.contoh.com",
    headers={"Authorization": f"Bearer {token}"},
    timeout=30.0,
) as client:
    r1 = client.get("/dokumen")        # koneksi dibuat
    r2 = client.get("/pengguna")       # koneksi yang SAMA dipakai lagi
    r3 = client.post("/cari", json={"q": "python"})

Kenapa ini penting: handshake TLS makan 100โ€“300 ms. Kalau kamu memanggil API yang sama seratus kali dengan client baru tiap kali, kamu membuang 10โ€“30 detik hanya untuk berjabat tangan berulang-ulang. Connection pooling menghapus biaya itu.

Timeout

httpx.Client(timeout=30.0)     # 30 detik untuk semua tahap
httpx.Client(timeout=None)     # tanpa batas โ€” jangan dipakai di production

# Kendali per tahap
httpx.Client(timeout=httpx.Timeout(
    connect=5.0,     # membuka koneksi
    read=60.0,       # menunggu byte berikutnya โ€” ini yang penting untuk LLM
    write=10.0,      # mengirim body
    pool=5.0,        # menunggu slot koneksi dari pool
))

# Override untuk satu request
client.get("/lambat", timeout=120.0)

Untuk streaming LLM, yang penting adalah read. Ia mengukur jeda antar potongan data, bukan total durasi. Jadi read=60 berarti "gagalkan kalau tidak ada byte baru selama 60 detik" โ€” respons yang berjalan 10 menit tetap aman selama datanya terus mengalir.

Menangani error

try:
    r = client.get("/data")
    r.raise_for_status()        # โ† WAJIB โ€” httpx tidak melempar otomatis
    data = r.json()
except httpx.HTTPStatusError as e:
    print(f"status {e.response.status_code}: {e.response.text}")
except httpx.TimeoutException:
    print("timeout")
except httpx.ConnectError:
    print("tidak bisa terhubung")
except httpx.HTTPError as e:
    print(f"error http: {e}")
ExceptionKapanLayak diulang?
ConnectErrorDNS gagal, koneksi ditolakYa
ConnectTimeoutGagal membuka koneksiYa
ReadTimeoutServer terlalu lama membalasYa
HTTPStatusErrorStatus 4xx / 5xxHanya 429 dan 5xx
RequestErrorInduk dari semua error jaringanโ€”

Mengirim data

client.post("/api", json={"nama": "Budi"})              # JSON
client.post("/form", data={"nama": "Budi"})             # form-encoded
client.post("/upload", files={"berkas": open("a.pdf", "rb")})
client.get("/cari", params={"q": "python", "limit": 10})

Streaming

with client.stream("GET", "/file-besar") as r:
    r.raise_for_status()
    for potongan in r.iter_bytes():
        tulis(potongan)

# Server-Sent Events โ€” bentuk yang dipakai LLM streaming
with client.stream("POST", "/chat", json=payload) as r:
    for baris in r.iter_lines():
        if baris.startswith("data: "):
            proses(json.loads(baris[6:]))

Retry ringan bawaan

transport = httpx.HTTPTransport(retries=3)     # HANYA untuk error koneksi
client = httpx.Client(transport=transport)

Ini tidak mengulang status 429 atau 5xx โ€” hanya kegagalan koneksi tingkat rendah. Untuk retry yang benar-benar berguna (dengan exponential backoff dan pemilihan status), pakai tenacity โ€” materi berikutnya.

Fitur lain yang berguna

client = httpx.Client(http2=True)              # butuh: uv add "httpx[http2]"
client = httpx.Client(follow_redirects=True)   # httpx TIDAK mengikuti redirect secara default
client = httpx.Client(proxy="http://proxy:8080")
client = httpx.Client(limits=httpx.Limits(max_connections=100, max_keepalive_connections=20))

Testing dengan mock transport

def handler(request: httpx.Request) -> httpx.Response:
    return httpx.Response(200, json={"hasil": "palsu"})

client = httpx.Client(transport=httpx.MockTransport(handler))
r = client.get("https://api.contoh.com/apa-saja")
r.json()    # {'hasil': 'palsu'} โ€” tidak ada jaringan yang tersentuh

Ini yang akan kamu pakai di Fase 6 untuk menguji kode tanpa memanggil API asli.

Perbedaan kecil dari requests

Perilakurequestshttpx
RedirectDiikuti otomatisTidak โ€” set follow_redirects=True
Timeout defaultTak terbatas5 detik
Nama parameterproxies=proxy=

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.