Kuasai fitur-fitur canggih Fetch API: intersepsi permintaan untuk modifikasi dinamis dan caching respons untuk meningkatkan performa aplikasi web global.
Fetch API Tingkat Lanjut: Intersepsi Permintaan vs. Caching Respons untuk Aplikasi Web Global
Dalam lanskap pengembangan web yang terus berkembang, performa dan responsivitas adalah yang terpenting. Bagi audiens global, di mana latensi jaringan dan stabilitas koneksi dapat sangat bervariasi, mengoptimalkan cara aplikasi kita mengambil dan menangani data bukan hanya praktik terbaik – itu adalah sebuah keharusan. Fetch API, standar modern untuk membuat permintaan jaringan di JavaScript, menawarkan kemampuan canggih yang melampaui permintaan GET dan POST sederhana. Di antara fitur-fitur canggih ini, intersepsi permintaan dan caching respons menonjol sebagai teknik penting untuk membangun aplikasi web global yang kuat dan efisien.
Postingan ini akan membahas secara mendalam tentang intersepsi permintaan dan caching respons menggunakan Fetch API. Kita akan menjelajahi konsep fundamentalnya, strategi implementasi praktis, dan bagaimana keduanya dapat dimanfaatkan secara sinergis untuk menciptakan pengalaman pengguna yang superior bagi pengguna di seluruh dunia. Kita juga akan membahas pertimbangan untuk internasionalisasi dan lokalisasi saat mengimplementasikan pola-pola ini.
Memahami Konsep Inti
Sebelum kita mendalami secara spesifik, mari kita perjelas apa yang dimaksud dengan intersepsi permintaan dan caching respons dalam konteks Fetch API.
Intersepsi Permintaan
Intersepsi permintaan mengacu pada kemampuan untuk mencegat permintaan jaringan keluar yang dibuat oleh kode JavaScript Anda sebelum dikirim ke server. Ini memungkinkan Anda untuk:
- Memodifikasi permintaan: Menambahkan header kustom (misalnya, token otentikasi, versi API), mengubah badan permintaan, mengubah URL, atau bahkan membatalkan permintaan dalam kondisi tertentu.
- Mencatat permintaan: Melacak aktivitas jaringan untuk tujuan debugging atau analitik.
- Membuat permintaan tiruan (mock): Menyimulasikan respons server selama pengembangan atau pengujian tanpa memerlukan backend yang aktif.
Meskipun Fetch API sendiri tidak menawarkan mekanisme bawaan langsung untuk mencegat permintaan seperti yang dilakukan oleh beberapa pustaka pihak ketiga atau intersep XMLHttpRequest (XHR) yang lebih lama, fleksibilitasnya memungkinkan kita untuk membangun pola intersepsi yang kuat, terutama melalui Service Worker.
Caching Respons
Caching respons, di sisi lain, melibatkan penyimpanan hasil permintaan jaringan secara lokal di sisi klien. Ketika permintaan berikutnya dibuat untuk sumber daya yang sama, respons yang di-cache dapat disajikan alih-alih melakukan panggilan jaringan baru. Ini menghasilkan peningkatan signifikan dalam:
- Performa: Pengambilan data yang lebih cepat mengurangi waktu muat dan meningkatkan responsivitas yang dirasakan.
- Dukungan Offline: Pengguna dapat mengakses data yang diambil sebelumnya bahkan ketika koneksi internet mereka tidak tersedia atau tidak stabil.
- Mengurangi Beban Server: Lalu lintas yang lebih sedikit ke server berarti biaya infrastruktur yang lebih rendah dan skalabilitas yang lebih baik.
Fetch API bekerja dengan lancar dengan mekanisme caching browser dan dapat lebih ditingkatkan dengan strategi caching kustom yang diimplementasikan melalui Service Worker atau API penyimpanan browser seperti localStorage atau IndexedDB.
Intersepsi Permintaan dengan Service Worker
Service Worker adalah landasan untuk mengimplementasikan pola intersepsi permintaan tingkat lanjut dengan Fetch API. Service Worker adalah file JavaScript yang berjalan di latar belakang, terpisah dari halaman web Anda, dan bertindak sebagai proksi jaringan yang dapat diprogram antara browser dan jaringan.
Apa itu Service Worker?
Sebuah Service Worker mendaftarkan dirinya untuk mendengarkan event, yang paling penting adalah event fetch. Ketika permintaan jaringan dibuat dari halaman yang dikontrol oleh Service Worker, Service Worker menerima event fetch dan kemudian dapat memutuskan bagaimana meresponsnya.
Mendaftarkan Service Worker
Langkah pertama adalah mendaftarkan Service Worker Anda. Ini biasanya dilakukan di file JavaScript utama Anda:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function(registration) {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch(function(error) {
console.error('Service Worker registration failed:', error);
});
}
Path /sw.js menunjuk ke skrip Service Worker Anda.
Skrip Service Worker (sw.js)
Di dalam file sw.js Anda, Anda akan mendengarkan event fetch:
self.addEventListener('fetch', function(event) {
// Logika intersepsi permintaan ada di sini
});
Mengimplementasikan Logika Intersepsi Permintaan
Di dalam listener event fetch, event.request menyediakan akses ke objek permintaan yang masuk. Anda kemudian dapat menggunakan ini untuk:
1. Memodifikasi Header Permintaan
Katakanlah Anda perlu menambahkan kunci API ke setiap permintaan keluar ke endpoint API tertentu. Anda dapat mencegat permintaan, membuat yang baru dengan header tambahan, dan kemudian melanjutkan:
self.addEventListener('fetch', function(event) {
const url = new URL(event.request.url);
const apiKey = 'YOUR_GLOBAL_API_KEY'; // Muat dari sumber yang aman atau konfigurasi
if (url.origin === 'https://api.example.com') {
// Kloning permintaan agar bisa kita modifikasi
const modifiedRequest = new Request(event.request, {
headers: {
'X-API-Key': apiKey,
// Anda juga dapat menggabungkan header yang ada:
// ...Object.fromEntries(event.request.headers.entries()),
// 'X-Custom-Header': 'value'
}
});
// Tanggapi dengan permintaan yang telah dimodifikasi
event.respondWith(fetch(modifiedRequest));
} else {
// Untuk permintaan lain, lanjutkan seperti biasa
event.respondWith(fetch(event.request));
}
});
Pertimbangan Global: Untuk aplikasi global, kunci API mungkin perlu spesifik per wilayah atau dikelola melalui layanan otentikasi pusat yang menangani perutean geografis. Pastikan logika intersepsi Anda mengambil atau menerapkan kunci yang sesuai untuk wilayah pengguna dengan benar.
2. Mengalihkan Permintaan
Anda mungkin ingin mengalihkan permintaan ke server yang berbeda berdasarkan lokasi pengguna atau strategi pengujian A/B.
self.addEventListener('fetch', function(event) {
const url = new URL(event.request.url);
const userLocation = getUserLocation(); // Placeholder untuk logika lokasi
if (url.pathname === '/api/data') {
let targetUrl = url.toString();
if (userLocation === 'europe') {
targetUrl = 'https://api.europe.example.com/data';
} else if (userLocation === 'asia') {
targetUrl = 'https://api.asia.example.com/data';
}
// Kloning dan alihkan
const redirectedRequest = new Request(targetUrl, {
method: event.request.method,
headers: event.request.headers,
body: event.request.body,
mode: 'cors'
});
event.respondWith(fetch(redirectedRequest));
} else {
event.respondWith(fetch(event.request));
}
});
function getUserLocation() {
// Di aplikasi nyata, ini akan melibatkan pencarian GeoIP, pengaturan pengguna, atau API geolokasi browser.
// Untuk demonstrasi, mari kita asumsikan logika sederhana.
return 'asia'; // Contoh
}
Pertimbangan Global: Pengalihan dinamis sangat penting untuk aplikasi global. Geo-routing dapat secara signifikan mengurangi latensi dengan mengarahkan pengguna ke server API terdekat. Implementasi `getUserLocation()` harus kuat, berpotensi menggunakan layanan geolokasi IP yang dioptimalkan untuk kecepatan dan akurasi di seluruh dunia.
3. Membatalkan Permintaan
Jika sebuah permintaan tidak lagi relevan (misalnya, pengguna menavigasi ke halaman lain), Anda mungkin ingin membatalkannya.
let ongoingRequests = {};
self.addEventListener('fetch', function(event) {
const requestId = Math.random().toString(36).substring(7);
ongoingRequests[requestId] = event.request;
event.respondWith(
fetch(event.request).finally(() => {
delete ongoingRequests[requestId];
})
);
});
// Contoh bagaimana Anda mungkin membatalkan permintaan dari thread utama (kurang umum untuk intersepsi itu sendiri, tetapi menunjukkan kontrol)
function cancelRequest(requestUrl) {
for (const id in ongoingRequests) {
if (ongoingRequests[id].url === requestUrl) {
// Catatan: Fetch API tidak memiliki 'abort' langsung untuk permintaan *setelah* dikirim melalui SW.
// Ini lebih bersifat ilustratif. Untuk pembatalan sejati, AbortController digunakan *sebelum* fetch.
console.warn(`Attempting to cancel request for: ${requestUrl}`);
// Pendekatan yang lebih praktis adalah memeriksa apakah permintaan masih relevan sebelum memanggil fetch di SW.
break;
}
}
}
Catatan: Pembatalan permintaan sejati setelah `fetch()` dipanggil di dalam Service Worker cukup rumit. `AbortController` API adalah cara standar untuk membatalkan permintaan `fetch`, tetapi perlu diteruskan ke panggilan `fetch` itu sendiri, yang sering kali dimulai dari thread utama. Service Worker terutama mencegat dan kemudian memutuskan bagaimana merespons.
4. Membuat Respons Tiruan untuk Pengembangan
Selama pengembangan, Anda dapat menggunakan Service Worker Anda untuk mengembalikan data tiruan (mock data), melewati jaringan yang sebenarnya.
self.addEventListener('fetch', function(event) {
const url = new URL(event.request.url);
if (url.pathname === '/api/users') {
// Periksa apakah ini permintaan GET
if (event.request.method === 'GET') {
const mockResponse = {
status: 200,
statusText: 'OK',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify([
{ id: 1, name: 'Alice', region: 'North America' },
{ id: 2, name: 'Bob', region: 'Europe' },
{ id: 3, name: 'Charlie', region: 'Asia' }
])
};
event.respondWith(new Response(mockResponse.body, mockResponse));
} else {
// Tangani metode lain jika perlu atau lanjutkan
event.respondWith(fetch(event.request));
}
} else {
event.respondWith(fetch(event.request));
}
});
Pertimbangan Global: Pembuatan data tiruan dapat mencakup variasi data yang relevan dengan berbagai wilayah, membantu pengembang menguji konten dan fitur yang dilokalkan tanpa memerlukan pengaturan backend global yang berfungsi penuh.
Strategi Caching Respons dengan Fetch API
Service Worker juga sangat kuat untuk mengimplementasikan strategi caching respons yang canggih. Di sinilah keajaiban dukungan offline dan pengambilan data secepat kilat benar-benar bersinar.
Memanfaatkan Cache Browser
Browser itu sendiri memiliki cache HTTP bawaan. Saat Anda menggunakan fetch() tanpa logika Service Worker khusus, browser akan terlebih dahulu memeriksa cache-nya. Jika respons cache yang valid dan belum kedaluwarsa ditemukan, itu akan disajikan secara langsung. Header kontrol cache yang dikirim oleh server (misalnya, Cache-Control: max-age=3600) menentukan berapa lama respons dianggap baru.
Caching Kustom dengan Service Worker
Service Worker memberi Anda kontrol terperinci atas caching. Pola umum melibatkan mencegat event fetch, mencoba mengambil respons dari cache, dan jika tidak ditemukan, mengambilnya dari jaringan dan kemudian menyimpannya di cache untuk penggunaan di masa mendatang.
1. Strategi Cache-First (Utamakan Cache)
Ini adalah strategi umum di mana Service Worker pertama-tama mencoba menyajikan respons dari cache-nya. Jika tidak ditemukan di cache, ia membuat permintaan jaringan, menyajikan respons dari jaringan, dan menyimpannya di cache untuk waktu berikutnya.
const CACHE_NAME = 'my-app-v1';
const urlsToCache = [
'/',
'/styles.css',
'/script.js'
];
self.addEventListener('install', function(event) {
// Lakukan langkah-langkah instalasi
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache ditemukan - kembalikan respons
if (response) {
return response;
}
// Tidak ada di cache - ambil dari jaringan
return fetch(event.request).then(
function(response) {
// Periksa apakah kita menerima respons yang valid
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// PENTING: Kloning respons. Respons adalah sebuah stream
// dan karena kita ingin browser mengonsumsi respons
// serta cache mengonsumsi respons, kita perlu
// mengkloningnya agar kita memiliki dua stream.
const responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
// Opsional: Bersihkan cache lama saat versi baru SW diinstal
self.addEventListener('activate', function(event) {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
2. Strategi Network-First (Utamakan Jaringan)
Strategi ini memprioritaskan pengambilan data baru dari jaringan. Jika permintaan jaringan gagal (misalnya, tidak ada koneksi), ia akan beralih ke respons yang di-cache.
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).catch(function() {
// Jika fetch gagal, kembali ke cache
return caches.match(event.request);
})
);
});
Pertimbangan Global: Network-first sangat baik untuk konten dinamis di mana kesegaran data sangat penting, tetapi Anda masih menginginkan ketahanan untuk pengguna dengan koneksi yang terputus-putus, yang umum di banyak bagian dunia.
3. Stale-While-Revalidate
Ini adalah strategi yang lebih canggih dan sering kali lebih disukai untuk konten dinamis. Strategi ini menyajikan respons yang di-cache secara langsung (membuat UI terasa cepat) sementara di latar belakang, ia membuat permintaan jaringan untuk memvalidasi ulang cache. Jika permintaan jaringan mengembalikan versi yang lebih baru, cache akan diperbarui.
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open(CACHE_NAME).then(function(cache) {
return cache.match(event.request).then(function(cachedResponse) {
// Jika respons cache ada, kembalikan segera
if (cachedResponse) {
// Mulai mengambil dari jaringan di latar belakang
fetch(event.request).then(function(networkResponse) {
// Jika respons jaringan valid, perbarui cache
if (networkResponse && networkResponse.status === 200 && networkResponse.type === 'basic') {
cache.put(event.request, networkResponse.clone());
}
}).catch(function() {
// Pengambilan dari jaringan gagal, tidak melakukan apa-apa, sudah disajikan dari cache
});
return cachedResponse;
}
// Tidak ada respons cache, ambil dari jaringan dan simpan di cache
return fetch(event.request).then(function(networkResponse) {
if (networkResponse && networkResponse.status === 200 && networkResponse.type === 'basic') {
cache.put(event.request, networkResponse.clone());
}
return networkResponse;
});
});
})
);
});
Pertimbangan Global: Strategi ini menawarkan yang terbaik dari kedua dunia – kecepatan yang dirasakan dan data terkini. Ini sangat efektif untuk aplikasi global di mana pengguna mungkin jauh dari server asal dan mengalami latensi tinggi; mereka mendapatkan data secara instan dari cache, dan cache diperbarui untuk permintaan berikutnya.
4. Strategi Cache-Only (Hanya Cache)
Strategi ini hanya menyajikan dari cache dan tidak pernah membuat permintaan jaringan. Ini ideal untuk aset penting yang tidak berubah atau ketika offline-first adalah persyaratan mutlak.
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
// Jika respons ditemukan di cache, kembalikan, jika tidak kembalikan kesalahan atau fallback
return response || new Response('Network error - Offline content not available', { status: 404 });
})
);
});
5. Strategi Network-Only (Hanya Jaringan)
Strategi ini hanya membuat permintaan jaringan dan tidak pernah menggunakan cache. Ini adalah perilaku default dari `fetch()` tanpa Service Worker, tetapi dapat didefinisikan secara eksplisit di dalam Service Worker untuk sumber daya tertentu.
self.addEventListener('fetch', function(event) {
event.respondWith(fetch(event.request));
});
Menggabungkan Intersepsi Permintaan dan Caching Respons
Kekuatan sejati dari Fetch API untuk aplikasi global muncul ketika Anda menggabungkan intersepsi permintaan dan caching respons. Service Worker Anda dapat bertindak sebagai pusat kendali, mengatur logika jaringan yang kompleks.
Contoh: Panggilan API Terotentikasi dengan Caching
Mari kita pertimbangkan aplikasi e-commerce. Data profil pengguna dan daftar produk mungkin dapat di-cache, tetapi tindakan seperti menambahkan item ke keranjang atau memproses pesanan memerlukan otentikasi dan harus ditangani secara berbeda.
// Di sw.js
const CACHE_NAME = 'my-app-v2';
// Cache aset statis
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
return cache.addAll([
'/', '/index.html', '/styles.css', '/app.js',
'/images/logo.png'
]);
})
);
});
self.addEventListener('fetch', function(event) {
const requestUrl = new URL(event.request.url);
// Tangani permintaan API
if (requestUrl.origin === 'https://api.globalstore.com') {
// Intersepsi Permintaan: Tambahkan Token Auth untuk panggilan API
const authHeader = { 'Authorization': `Bearer ${getAuthToken()}` }; // Placeholder
const modifiedRequest = new Request(event.request, {
headers: {
...Object.fromEntries(event.request.headers.entries()),
...authHeader
}
});
// Strategi Caching Respons: Stale-While-Revalidate untuk katalog produk
if (requestUrl.pathname.startsWith('/api/products')) {
event.respondWith(
caches.open(CACHE_NAME).then(function(cache) {
return cache.match(modifiedRequest).then(function(cachedResponse) {
// Jika respons cache ada, kembalikan segera
if (cachedResponse) {
// Mulai mengambil dari jaringan di latar belakang untuk pembaruan
fetch(modifiedRequest).then(function(networkResponse) {
if (networkResponse && networkResponse.status === 200 && networkResponse.type === 'basic') {
cache.put(modifiedRequest, networkResponse.clone());
}
}).catch(function() { /* Abaikan kesalahan jaringan di sini */ });
return cachedResponse;
}
// Tidak ada respons cache, ambil dari jaringan dan simpan di cache
return fetch(modifiedRequest).then(function(networkResponse) {
if (networkResponse && networkResponse.status === 200 && networkResponse.type === 'basic') {
cache.put(modifiedRequest, networkResponse.clone());
}
return networkResponse;
});
});
})
);
}
// Network-First untuk data spesifik pengguna (misalnya, keranjang, pesanan)
else if (requestUrl.pathname.startsWith('/api/user') || requestUrl.pathname.startsWith('/api/cart')) {
event.respondWith(
fetch(modifiedRequest).catch(function() {
// Fallback ke cache jika jaringan gagal (untuk melihat data yang dimuat sebelumnya secara offline)
return caches.match(modifiedRequest);
})
);
}
// Network-only untuk operasi penting (misalnya, melakukan pemesanan)
else {
event.respondWith(fetch(modifiedRequest));
}
}
// Untuk permintaan lain (misalnya, aset eksternal), gunakan fetch default
else {
event.respondWith(fetch(event.request));
}
});
function getAuthToken() {
// Fungsi ini perlu mengambil token auth, berpotensi dari localStorage
// atau cookie. Hati-hati dengan implikasi keamanan.
return localStorage.getItem('authToken') || 'guest'; // Contoh
}
Pertimbangan Global:
- Otentikasi: `getAuthToken()` harus kuat. Untuk aplikasi global, penyedia identitas pusat yang menangani OAuth atau JWT adalah hal yang umum. Pastikan token disimpan dan diakses dengan aman.
- Endpoint API: Contoh di atas mengasumsikan satu domain API. Kenyataannya, Anda mungkin memiliki API regional, dan logika intersepsi harus memperhitungkan ini, berpotensi menggunakan URL permintaan untuk menentukan domain API mana yang akan dituju.
- Tindakan Pengguna Offline: Untuk tindakan seperti menambahkan ke keranjang secara offline, Anda biasanya akan mengantrekan tindakan di
IndexedDBdan menyinkronkannya saat koneksi pulih. Service Worker dapat mendeteksi status online/offline dan mengelola antrean ini.
Mengimplementasikan Caching untuk Konten Internasionalisasi
Ketika berhadapan dengan audiens global, aplikasi Anda kemungkinan besar menyajikan konten dalam berbagai bahasa dan wilayah. Strategi caching perlu mengakomodasi hal ini.
Memvariasikan Respons Berdasarkan Header
Saat melakukan caching konten yang diinternasionalkan, sangat penting untuk memastikan bahwa respons yang di-cache cocok dengan preferensi bahasa dan lokal permintaan. Header Accept-Language adalah kuncinya di sini. Anda dapat menggunakannya dalam panggilan caches.match Anda.
// Di dalam handler event fetch di sw.js
self.addEventListener('fetch', function(event) {
const request = event.request;
const url = new URL(request.url);
if (url.pathname.startsWith('/api/content')) {
event.respondWith(
caches.open(CACHE_NAME).then(function(cache) {
// Buat kunci yang menyertakan header Accept-Language untuk entri cache yang bervariasi
const cacheKey = new Request(request.url, {
headers: {
'Accept-Language': request.headers.get('Accept-Language') || 'en-US'
}
});
return cache.match(cacheKey).then(function(cachedResponse) {
if (cachedResponse) {
console.log('Serving from cache for locale:', request.headers.get('Accept-Language'));
// Berpotensi memvalidasi ulang di latar belakang jika stale-while-revalidate
return cachedResponse;
}
// Ambil dari jaringan dan cache dengan kunci spesifik lokal
return fetch(request).then(function(networkResponse) {
if (networkResponse.ok) {
// Kloning respons untuk caching
const responseToCache = networkResponse.clone();
cache.put(cacheKey, responseToCache);
}
return networkResponse;
});
});
})
);
} else {
event.respondWith(fetch(request));
}
});
Pertimbangan Global:
- Header `Accept-Language`: Pastikan backend Anda memproses header `Accept-Language` dengan benar untuk menyajikan konten yang dilokalkan dengan sesuai. Sisi klien (browser) sering kali mengirim header ini secara otomatis berdasarkan pengaturan OS/browser pengguna.
- Header `Vary`: Saat menyajikan konten dari server yang perlu menghormati caching berdasarkan header seperti `Accept-Language`, pastikan server menyertakan header `Vary: Accept-Language` dalam responsnya. Ini memberitahu cache perantara (termasuk cache HTTP browser dan cache Service Worker) bahwa konten respons dapat bervariasi berdasarkan header ini.
- Konten Dinamis vs. Aset Statis: Aset statis seperti gambar atau font mungkin tidak perlu bervariasi berdasarkan lokal, menyederhanakan caching mereka. Namun, konten dinamis sangat diuntungkan dari caching yang sadar lokal.
Alat dan Pustaka
Meskipun Anda dapat membangun logika intersepsi permintaan dan caching yang canggih secara langsung dengan Service Worker dan Fetch API, beberapa pustaka dapat menyederhanakan prosesnya:
- Workbox: Satu set pustaka dan alat dari Google yang memudahkan implementasi Service Worker yang kuat. Ini menyediakan strategi caching siap pakai, perutean, dan utilitas bermanfaat lainnya, secara signifikan mengurangi kode boilerplate. Workbox mengabstraksi sebagian besar kompleksitas siklus hidup Service Worker dan manajemen cache.
- Axios: Meskipun tidak terkait langsung dengan Service Worker, Axios adalah klien HTTP populer yang menawarkan interseptor bawaan untuk permintaan dan respons. Anda dapat menggunakan Axios bersama dengan Service Worker untuk manajemen permintaan jaringan sisi klien yang lebih efisien.
Contoh dengan Workbox
Workbox secara signifikan menyederhanakan strategi caching:
// Di sw.js (menggunakan Workbox)
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.0.0/workbox-sw.js');
const CACHE_NAME = 'my-app-v2';
// Pre-cache aset penting
workbox.precaching.precacheAndRoute([
'/', '/index.html', '/styles.css', '/app.js',
'/images/logo.png'
]);
// Cache permintaan API dengan stale-while-revalidate
workbox.routing.registerRoute(
/https:\/\/api\.globalstore\.com\/api\/products/, // Regex untuk mencocokkan URL API produk
new workbox.strategies.StaleWhileRevalidate({
cacheName: CACHE_NAME,
plugins: [
// Opsional tambahkan caching untuk lokal yang berbeda jika diperlukan
// new workbox.cacheableResponse.CacheableResponsePlugin({
// statuses: [0, 200]
// })
]
})
);
// Cache data spesifik pengguna dengan strategi network-first
workbox.routing.registerRoute(
/https:\/\/api\.globalstore\.com\/api\/(user|cart)/, // Regex untuk API pengguna/keranjang
new workbox.strategies.NetworkFirst({
cacheName: CACHE_NAME,
plugins: [
new workbox.expiration.ExpirationPlugin({
// Cache hanya 5 entri, kedaluwarsa setelah 30 hari
maxEntries: 5,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 hari
}),
new workbox.cacheableResponse.CacheableResponsePlugin({
statuses: [0, 200]
})
]
})
);
// Network-only untuk operasi penting (contoh)
workbox.routing.registerRoute(
/https:\/\/api\.globalstore\.com\/api\/order/,
new workbox.strategies.NetworkOnly()
);
// Handler kustom untuk menambahkan header Authorization ke semua permintaan API
workbox.routing.registerRoute(
/https:\/\/api\.globalstore\.com/,
new workbox.strategies.NetworkFirst({
cacheName: CACHE_NAME,
plugins: [
{
requestWillFetch: async ({ request, url, event, delta }) => {
const token = localStorage.getItem('authToken');
const headers = new Headers(request.headers);
if (token) {
headers.set('Authorization', `Bearer ${token}`);
}
return new Request(url, { ...request, headers });
}
}
]
})
);
Pertimbangan Global: Konfigurasi Workbox dapat disesuaikan untuk kebutuhan internasional. Misalnya, Anda dapat menggunakan perutean canggih Workbox untuk menyajikan versi cache yang berbeda berdasarkan bahasa atau wilayah pengguna yang terdeteksi, membuatnya sangat mudah beradaptasi untuk basis pengguna global.
Praktik Terbaik dan Pertimbangan untuk Aplikasi Global
Saat mengimplementasikan intersepsi permintaan dan caching respons untuk audiens global, ingatlah praktik terbaik berikut:
- Peningkatan Progresif (Progressive Enhancement): Pastikan aplikasi Anda berfungsi bahkan tanpa fitur canggih seperti Service Worker. Fungsionalitas inti harus berfungsi di browser lama dan di lingkungan di mana Service Worker mungkin tidak didukung.
- Keamanan: Berhati-hatilah saat menangani data sensitif seperti token otentikasi selama intersepsi permintaan. Simpan token dengan aman (misalnya, menggunakan cookie HttpOnly jika sesuai, atau mekanisme penyimpanan aman). Jangan pernah melakukan hardcode rahasia.
- Invalidasi Cache: Menerapkan strategi invalidasi cache yang kuat sangat penting. Data basi bisa lebih buruk daripada tidak ada data sama sekali. Pertimbangkan kedaluwarsa berbasis waktu, versioning, dan invalidasi berbasis event.
- Pemantauan Performa: Terus pantau performa aplikasi Anda di berbagai wilayah dan kondisi jaringan. Alat seperti Lighthouse, WebPageTest, dan RUM (Real User Monitoring) sangat berharga.
- Penanganan Kesalahan: Rancang logika intersepsi dan caching Anda untuk menangani kesalahan jaringan, masalah server, dan respons tak terduga dengan baik. Sediakan pengalaman fallback yang berarti bagi pengguna.
- Pentingnya Header `Vary`: Untuk respons cache yang bergantung pada header permintaan (seperti `Accept-Language`), pastikan backend Anda mengirim header `Vary` dengan benar. Ini fundamental untuk perilaku caching yang benar di berbagai preferensi pengguna.
- Optimisasi Sumber Daya: Hanya cache apa yang diperlukan. Aset besar yang jarang berubah adalah kandidat yang baik untuk caching agresif. Data dinamis yang sering berubah memerlukan strategi caching yang lebih dinamis.
- Ukuran Bundle: Perhatikan ukuran skrip Service Worker Anda sendiri. SW yang terlalu besar bisa lambat untuk diinstal dan diaktifkan, berdampak pada pengalaman pengguna awal.
- Kontrol Pengguna: Pertimbangkan untuk memberikan pengguna beberapa kontrol atas perilaku caching jika berlaku, meskipun ini kurang umum untuk aplikasi web biasa.
Kesimpulan
Intersepsi permintaan dan caching respons, terutama ketika didukung oleh Service Worker dan Fetch API, adalah alat yang sangat diperlukan untuk membangun aplikasi web global yang berkinerja tinggi dan tangguh. Dengan mencegat permintaan, Anda mendapatkan kontrol atas bagaimana aplikasi Anda berkomunikasi dengan server, memungkinkan penyesuaian dinamis untuk otentikasi, perutean, dan lainnya. Dengan menerapkan strategi caching yang cerdas, Anda secara drastis meningkatkan waktu muat, mengaktifkan akses offline, dan mengurangi beban server.
Bagi audiens internasional, teknik-teknik ini bukan sekadar optimisasi; mereka adalah dasar untuk memberikan pengalaman pengguna yang konsisten dan positif, terlepas dari lokasi geografis atau kondisi jaringan. Baik Anda membangun platform e-commerce global, portal berita yang kaya konten, atau aplikasi SaaS, menguasai kemampuan canggih Fetch API akan membedakan aplikasi Anda.
Ingatlah untuk memanfaatkan alat seperti Workbox untuk mempercepat pengembangan dan memastikan strategi Anda kuat. Terus uji dan pantau performa aplikasi Anda di seluruh dunia untuk menyempurnakan pendekatan Anda dan memberikan pengalaman terbaik bagi setiap pengguna.