FRONTEND WEB-DEVELOPMENT JAVASCRIPT TYPESCRIPT FETCH-API AXIOS API-INTEGRATION MIDDLEWARE HTTP BEST-PRACTICES CODE-QUALITY AUTHENTICATION ERROR-HANDLING CACHING PERFORMANCE-OPTIMIZATION DEVELOPER-EXPERIENCE

HTTP Interceptors di Frontend: Membangun Lapisan Middleware Cerdas untuk Fetch API dan Axios

⏱️ 12 menit baca
👨‍💻

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:

Jika Anda melakukan ini secara manual di setiap pemanggilan fetch atau axios.get(), kode Anda akan menjadi:

📌 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:

  1. Request Interceptor: Berjalan sebelum request dikirim ke server. Di sini Anda bisa memodifikasi request (misalnya, menambahkan header, mengubah URL, mengubah payload).
  2. 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).

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:

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:

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:

  1. 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.
  2. Logging & Monitoring:
    • Mencatat setiap request dan response (URL, method, status, waktu).
    • Mengirim data log ke layanan monitoring (misalnya, Sentry atau custom logger).
  3. 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).
  4. 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.
  5. 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).
  6. 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:

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