Pola Interceptor: Auth dan Logging Terpusat
Membangun wrapper fetch dengan interceptor: auth header otomatis, refresh token, dan logging terpusat.
Pramugari yang memeriksa tiket sebelum boarding
Di bandara, kamu tidak menunjukkan tiket ke setiap petugas; ada satu gerbang pemeriksaan yang dilalui semua penumpang. Interceptor adalah "gerbang" untuk request HTTP: satu tempat yang dilalui setiap request untuk disuntik auth header, dan dilalui setiap response untuk logging atau penanganan error global.
Kenapa pola ini lahir
Tanpa interceptor, setiap fungsi fetch mengulang kode yang sama: ambil token, pasang header, cek 401, log error. Lupa di satu tempat berarti bug keamanan. Dengan wrapper terpusat, aturan ditulis sekali dan berlaku untuk seluruh aplikasi. Library seperti axios punya interceptor bawaan; dengan fetch murni, kamu membuatnya sendiri (dan memahaminya lebih dalam).
Contoh 1: request interceptor (suntik auth)
async function api(path, options = {}) {
const token = localStorage.getItem("token");
const headers = {
"Content-Type": "application/json",
...(token ? { "Authorization": "Bearer " + token } : {}),
...(options.headers || {})
};
console.log("[API]", options.method || "GET", path);
return fetch("https://api.contoh.id" + path, { ...options, headers });
}Setiap pemanggilan api("/profil") otomatis membawa token dan tercatat di log. Pemanggil tidak perlu tahu soal auth sama sekali.
Contoh 2: response interceptor (refresh terpusat)
async function apiTahan(path, options = {}) {
let res = await api(path, options);
if (res.status === 401) {
const ok = await refreshToken();
if (!ok) {
window.location.href = "/login";
throw new Error("Sesi berakhir");
}
res = await api(path, options);
}
if (!res.ok) {
console.error("[API ERROR]", res.status, path);
throw new Error("Request gagal: " + res.status);
}
return res.json();
}Logika refresh token yang tadinya tersebar kini ada di satu tempat. Semua fitur aplikasi otomatis mendapat silent refresh tanpa kode tambahan.
Kesalahan umum
Salah: interceptor yang saling menimpa header. Urutan spread salah membuat header custom hilang. Yang benar: header default dulu, lalu header pemanggil menimpa (seperti contoh).
Salah: infinite loop refresh. Interceptor refresh memanggil dirinya sendiri saat refresh gagal. Yang benar: tandai request refresh agar tidak masuk interceptor lagi, atau pisahkan fungsi refresh dari wrapper.
Salah: logging token di production. Console log berisi Authorization header bocor ke siapa pun yang membuka DevTools. Yang benar: log hanya di development, atau sensor nilai sensitif.
Menambahkan retry ke interceptor
Gabungkan interceptor dengan retry cerdas dari modul error handling:
async function apiTahan(path, options = {}, maxCoba = 3) {
let jeda = 1000;
for (let i = 1; i <= maxCoba; i++) {
try {
const res = await api(path, options);
if (res.ok) return await res.json();
if (res.status < 500) throw new Error("HTTP " + res.status);
} catch (e) {
if (i === maxCoba) throw e;
await new Promise(function(r) { setTimeout(r, jeda); });
jeda *= 2;
}
}
}Satu fungsi kini menangani auth otomatis, logging, DAN retry dengan backoff. Inilah "pintu tunggal" yang ideal: seluruh aplikasi memanggil apiTahan, dan semua kebijakan HTTP terpusat di dua fungsi kecil.
Catatan teknis: Pola ini adalah versi sederhana dari middleware. Di framework besar, konsep yang sama muncul sebagai axios interceptors, Angular HttpInterceptor, atau middleware Express. Kuasai polanya sekali, berlaku di mana-mana.
Tantangan
Buat mini interceptor sendiri
Buat fungsi api() yang:
- Otomatis menambahkan header Authorization dari localStorage.
- Me-log setiap request (method + path).
- Melempar error dengan pesan jelas untuk status >= 400.
Tulis sebagai satu blok JavaScript + satu contoh pemanggilan.