HTTP Interceptors di Frontend: Membangun Lapisan Middleware Cerdas untuk Fetch API dan Axios
Sebagai developer web, berinteraksi dengan API backend adalah bagian tak terpisahkan dari pekerjaan kita. Mulai dari mengambil data user, mengirim form, hingga mengunggah file, semua melibatkan request HTTP. Namun, seiring kompleksitas aplikasi yang meningkat, kita seringkali menemukan diri kita mengulang kode yang sama berulang kali: menambahkan header autentikasi, menangani error secara spesifik, atau melakukan logging.
Di sinilah HTTP Interceptors hadir sebagai penyelamat. Interceptor memungkinkan kita untuk “mencegat” (intercept) request dan response HTTP sebelum dikirim atau sebelum diterima oleh aplikasi. Ini seperti memiliki pos pemeriksaan cerdas yang bisa memodifikasi, menambah, atau bahkan memblokir lalu lintas HTTP secara terpusat.
Artikel ini akan membahas secara mendalam bagaimana Anda bisa memanfaatkan HTTP Interceptors di frontend, baik dengan Fetch API bawaan browser maupun library populer seperti Axios. Mari kita mulai!
1. Pendahuluan: Mengapa Kita Butuh Interceptor?
Bayangkan Anda sedang membangun aplikasi web yang memiliki puluhan, atau bahkan ratusan, endpoint API. Setiap kali Anda melakukan request, Anda perlu:
- Menambahkan token autentikasi ke header.
- Menangani error jaringan atau error dari server (misalnya, menampilkan notifikasi toast).
- Melakukan logging untuk setiap request yang keluar dan response yang masuk.
- Mungkin juga ada logika caching atau transformasi data tertentu.
Jika Anda melakukan ini secara manual di setiap pemanggilan fetch atau axios.get(), kode Anda akan menjadi:
- Berulang (Repetitive): Banyak duplikasi kode untuk tugas yang sama.
- Sulit Dirawat (Hard to Maintain): Jika ada perubahan pada cara autentikasi, Anda harus mengubah di puluhan tempat.
- Rentan Error: Mudah lupa menambahkan logika penting di satu tempat.
📌 Masalahnya: Tanpa interceptor, logika yang seharusnya bersifat cross-cutting concern (berlaku di banyak tempat) malah tersebar di seluruh codebase, membuat aplikasi sulit dikelola dan di-debug.
Dengan interceptor, kita bisa memusatkan logika ini, menjadikannya lebih bersih, mudah di-maintain, dan lebih robust.
2. Apa Itu HTTP Interceptor? Analogi Pos Satpam
Secara sederhana, HTTP Interceptor adalah sebuah fungsi yang bisa Anda daftarkan untuk berjalan setiap kali request HTTP dikirim atau response HTTP diterima. Ada dua jenis utama:
- Request Interceptor: Berjalan sebelum request dikirim ke server. Di sini Anda bisa memodifikasi request (misalnya, menambahkan header, mengubah URL, mengubah payload).
- Response Interceptor: Berjalan setelah response diterima dari server, tapi sebelum response tersebut sampai ke kode aplikasi Anda. Di sini Anda bisa memodifikasi response (misalnya, mengubah format data, menangani error status code).
💡 Analogi Pos Satpam: Bayangkan Anda ingin mengirim surat (request) ke kantor pos (server).
- Request Interceptor adalah satpam di gerbang rumah Anda. Setiap surat yang keluar, dia akan memastikan sudah ada prangko (token autentikasi) dan alamat yang benar. Jika ada yang salah, dia bisa menolak surat itu atau memperbaikinya.
- Response Interceptor adalah satpam di gerbang rumah Anda yang menunggu surat balasan (response). Setiap surat balasan yang datang, dia akan memeriksa apakah ada cap pos yang aneh (error status code) atau apakah suratnya sudah dalam amplop yang rapi (format data yang konsisten) sebelum memberikannya kepada Anda.
Dengan satpam ini, Anda tidak perlu lagi khawatir tentang detail-detail kecil setiap kali mengirim atau menerima surat. Tugas-tugas berulang sudah ditangani secara otomatis.
3. Interceptor dengan Fetch API (Vanilla JavaScript)
Fetch API adalah standar modern untuk melakukan request HTTP di browser. Sayangnya, Fetch API secara native tidak menyediakan mekanisme interceptor bawaan seperti halnya Axios. Ini berarti kita harus sedikit lebih kreatif untuk mengimplementasikan pola interceptor.
Cara paling umum adalah dengan membuat fungsi fetch pembungkus (wrapper) custom Anda sendiri.
Contoh: Menambahkan Header Autentikasi ke Setiap Request
Misalkan Anda memiliki token autentikasi yang disimpan di localStorage. Anda ingin setiap request yang keluar memiliki header Authorization: Bearer <token>.
// authService.js
const getToken = () => localStorage.getItem('authToken');
// fetchWrapper.js
const fetchWithInterceptor = async (url, options = {}) => {
const token = getToken();
const newHeaders = {
'Content-Type': 'application/json',
...options.headers, // Gabungkan header yang sudah ada
};
if (token) {
newHeaders['Authorization'] = `Bearer ${token}`;
}
const newOptions = {
...options,
headers: newHeaders,
};
try {
const response = await fetch(url, newOptions);
// Tangani error response secara global di sini (response interceptor)
if (!response.ok) {
const errorData = await response.json();
console.error('API Error:', response.status, errorData);
// Contoh: Redirect ke halaman login jika token expired
if (response.status === 401 && errorData.message === 'Token Expired') {
alert('Sesi Anda berakhir. Silakan login kembali.');
window.location.href = '/login';
}
throw new Error(errorData.message || 'Something went wrong');
}
return response;
} catch (error) {
console.error('Network or Fetch Error:', error);
// Contoh: Tampilkan notifikasi toast untuk error jaringan
alert('Terjadi masalah koneksi. Mohon coba lagi.');
throw error; // Lempar error agar bisa ditangkap di tempat pemanggilan
}
};
// Penggunaan di komponen/aplikasi Anda
async function getUserData() {
try {
const response = await fetchWithInterceptor('/api/users/me');
const userData = await response.json();
console.log('User Data:', userData);
} catch (error) {
console.error('Failed to get user data:', error.message);
}
}
getUserData();
Dalam contoh ini:
fetchWithInterceptorberfungsi sebagai request interceptor dengan menambahkan headerAuthorization.- Bagian
if (!response.ok)dancatchdi dalamfetchWithInterceptorberfungsi sebagai response interceptor untuk menangani error secara terpusat.
✅ Kelebihan: Tidak perlu library tambahan, kontrol penuh.
❌ Kekurangan: Lebih banyak boilerplate, perlu konsisten menggunakan fetchWithInterceptor di semua tempat.
4. Interceptor dengan Axios (Library Populer)
Axios adalah library HTTP client berbasis Promise yang sangat populer di ekosistem JavaScript. Salah satu fitur andalannya adalah mekanisme interceptor bawaan yang powerful dan mudah digunakan.
Contoh: Refresh Token Otomatis dan Logging
Bayangkan Anda menggunakan token akses yang bisa kadaluarsa, dan Anda memiliki token refresh untuk mendapatkan token akses baru tanpa user harus login ulang. Axios bisa menangani ini dengan anggun.
Pertama, instal Axios jika belum: npm install axios atau yarn add axios.
// api.js (konfigurasi Axios instance)
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://api.contoh.com', // Ganti dengan URL API Anda
timeout: 10000, // Timeout 10 detik
headers: {
'Content-Type': 'application/json',
},
});
// 🎯 Request Interceptor
instance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
console.log(`➡️ Mengirim request ke: ${config.url}`);
return config;
},
(error) => {
console.error('❌ Request error:', error);
return Promise.reject(error);
}
);
// 🎯 Response Interceptor
instance.interceptors.response.use(
(response) => {
console.log(`⬅️ Menerima response dari: ${response.config.url} dengan status: ${response.status}`);
return response;
},
async (error) => {
const originalRequest = error.config;
// ⚠️ Contoh: Refresh Token Otomatis
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true; // Tandai request ini sudah dicoba ulang
try {
const refreshToken = localStorage.getItem('refreshToken');
if (!refreshToken) {
// Tidak ada refresh token, redirect ke login
alert('Sesi Anda berakhir. Silakan login kembali.');
window.location.href = '/login';
return Promise.reject(error);
}
// Panggil endpoint refresh token
const { data } = await axios.post('/auth/refresh-token', { refreshToken });
localStorage.setItem('accessToken', data.accessToken);
// Update header Authorization di request asli dan coba lagi
originalRequest.headers.Authorization = `Bearer ${data.accessToken}`;
return instance(originalRequest); // Kirim ulang request asli
} catch (refreshError) {
console.error('❌ Gagal refresh token:', refreshError);
alert('Sesi Anda berakhir. Silakan login kembali.');
window.location.href = '/login';
return Promise.reject(refreshError);
}
}
// Tangani error lain
console.error(`❌ Response error dari ${originalRequest.url}: ${error.response.status}`, error.response.data);
alert(`Terjadi error: ${error.response.data.message || 'Server error'}`);
return Promise.reject(error);
}
);
export default instance; // Export instance Axios yang sudah dikonfigurasi
// Penggunaan di komponen/aplikasi Anda
import api from './api';
async function getProducts() {
try {
const response = await api.get('/products');
console.log('Products:', response.data);
} catch (error) {
// Error sudah ditangani di interceptor, ini untuk error yang tidak tertangani atau untuk logika spesifik
console.error('Failed to fetch products:', error.message);
}
}
getProducts();
Dalam contoh ini:
- Request Interceptor: Menambahkan header
Authorizationdan melakukan logging sebelum request dikirim. - Response Interceptor:
- Melakukan logging setelah response diterima.
- Secara cerdas mendeteksi error
401 Unauthorized(biasanya karena token kadaluarsa). - Jika terjadi
401, ia akan mencoba menggunakanrefreshTokenuntuk mendapatkanaccessTokenbaru. - Jika berhasil, request asli akan dikirim ulang secara otomatis.
- Jika gagal refresh token, user akan diarahkan ke halaman login.
- Menangani error lain dengan menampilkan alert.
✅ Kelebihan: Sangat powerful, sintaksis bersih, manajemen error dan autentikasi yang kompleks menjadi mudah. ❌ Kekurangan: Menambah dependensi library.
5. Kasus Penggunaan Umum Interceptor
Interceptor bukan hanya untuk autentikasi. Banyak skenario yang bisa dioptimalkan:
- Autentikasi & Otorisasi:
- Menambahkan token JWT/Bearer ke setiap request.
- Menangani refresh token otomatis (seperti contoh Axios di atas).
- Menambahkan API Key ke header atau query parameter.
- Logging & Monitoring:
- Mencatat setiap request dan response (URL, method, status, waktu).
- Mengirim data log ke layanan monitoring (misalnya, Sentry atau custom logger).
- Error Handling Global:
- Menampilkan notifikasi toast/alert untuk error HTTP umum (4xx, 5xx).
- Mengarahkan user ke halaman maintenance jika server down (503).
- Mengimplementasikan retry logic untuk error sementara (misalnya, 500, timeout).
- Caching Client-Side:
- Mengecek cache sebelum mengirim request. Jika data ada di cache dan masih valid, langsung kembalikan dari cache.
- Menyimpan response ke cache setelah diterima.
- Transformasi Data:
- Mengubah format request payload (misalnya, dari camelCase ke snake_case sebelum dikirim ke backend).
- Mengubah format response data (misalnya, dari snake_case ke camelCase setelah diterima dari backend).
- Pengelolaan Loading State:
- Menampilkan spinner loading global saat request sedang berjalan.
- Menyembunyikan spinner saat semua request selesai atau ada error.
6. Best Practices dan Pertimbangan
Ketika menggunakan interceptor, ada beberapa hal yang perlu diperhatikan:
- Urutan Interceptor: Jika Anda memiliki beberapa interceptor, urutan pendaftarannya sangat penting. Interceptor akan dieksekusi sesuai urutan pendaftarannya. Misalnya, interceptor logging harus sebelum interceptor autentikasi jika Anda ingin mencatat request sebelum token ditambahkan.
- Hindari Side Effects yang Tidak Perlu: Interceptor harus sefokus mungkin pada tugasnya. Hindari melakukan komputasi berat atau operasi yang tidak terkait langsung dengan modifikasi request/response.
- Penanganan Error di Interceptor: Pastikan Anda selalu mengembalikan
Promise.reject(error)di blokcatchinterceptor agar error tetap diteruskan ke pemanggil. Jika tidak, error bisa “hilang”. - Testability: Pastikan interceptor Anda mudah diuji. Pisahkan logika kompleks menjadi fungsi-fungsi kecil yang bisa di-mock.
- Kapan Tidak Perlu Interceptor?: Untuk aplikasi yang sangat sederhana dengan sedikit request dan logika API yang minimal, mungkin overhead implementasi interceptor tidak sepadan. Namun, begitu aplikasi mulai tumbuh, interceptor akan sangat membantu.
- Axios Instance: Selalu buat instance Axios baru (
axios.create()) untuk setiap set konfigurasi interceptor yang berbeda, terutama jika Anda berinteraksi dengan beberapa API yang memiliki kebutuhan interceptor berbeda.
Kesimpulan
HTTP Interceptors adalah pola desain yang sangat efektif untuk mengelola interaksi HTTP di aplikasi frontend Anda. Dengan memusatkan logika seperti autentikasi, error handling, dan logging, Anda dapat secara drastis meningkatkan kebersihan kode, maintainability, dan robustness aplikasi Anda.
Baik Anda memilih untuk membangun wrapper custom di atas Fetch API atau memanfaatkan fitur bawaan Axios yang powerful, memahami dan menerapkan interceptor akan membawa kualitas codebase Anda ke level berikutnya. Mulailah mengimplementasikannya di proyek Anda berikutnya dan rasakan perbedaannya!
🔗 Baca Juga
- Menguasai Fetch API: Jurus Rahasia Data Fetching Modern yang Robust dan Efisien
- Mengelola Retry dan Exponential Backoff untuk HTTP Request di Frontend: Membangun Aplikasi Web yang Tahan Banting
- Mengamankan Autentikasi di SPA: Strategi Implementasi Refresh Token dan Session Management
- Mengelola Error di Aplikasi Web Modern: Strategi Praktis untuk Kode yang Robust dan Mudah Didebug