Caching di Next.js
Memahami 4 lapis cache: request memoization, data cache, full route, router.
Kenapa Next.js punya 4 lapis cache?
Bayangkan warung kopi langgananmu. Menu-menu populer difotokopi dan disimpan di laci kasir (cepat diambil), stok bahan dicatat di buku (agak lama dicari), dan kalau kehabisan barulah menelepon pemasok (paling lama). Setiap lapis punya kecepatan dan kesegaran yang berbeda. Next.js berpikir sama: mengambil data dari API atau database itu mahal (latensi, rate limit, biaya), jadi hasilnya disimpan di beberapa lapis cache dengan aturan berbeda.
Kenapa konsep ini ada? Tanpa cache, setiap pengunjung memicu fetch baru ke API-mu. Sepuluh ribu pengunjung berarti sepuluh ribu request ke database, dan tagihan (atau rate limit) meledak. Cache membuat satu fetch melayani banyak pengunjung.
4 lapis cache, dari dalam ke luar
- Request Memoization: dalam SATU render,
fetchke URL yang sama hanya dieksekusi sekali oleh React, hasilnya dibagi ke semua komponen yang meminta. Lapis paling dalam, dibahas tuntas di modul request-memoization. - Data Cache: hasil
fetchdisimpan di server dan dipakai ulang ANTAR request, bahkan antar pengunjung berbeda. Ini lapis yang paling sering bikin bingung ("kok dataku tidak update?"). - Full Route Cache: HTML dan payload React Server Component hasil render disimpan, jadi request berikutnya tidak perlu render ulang.
- Router Cache: cache di browser (client-side) yang membuat navigasi antar halaman terasa instan.
Saat ada request, Next.js mengecek dari lapis terluar ke dalam: Router Cache dulu, lalu Full Route Cache, lalu Data Cache, terakhir fetch sungguhan.
Mengontrol Data Cache lewat fetch
// lib/api.ts
// Contoh 1: tiga strategi untuk tiga kebutuhan berbeda
// A. Cache selamanya (default bila tanpa opsi): untuk data yang hampir
// tidak berubah, mis. daftar negara atau kategori.
export async function getCountries() {
const res = await fetch("https://api.example.com/countries");
return res.json();
}
// B. Selalu segar: untuk data real-time seperti stok atau harga live.
export async function getLiveStock() {
const res = await fetch("https://api.example.com/stock", {
cache: "no-store",
});
return res.json();
}
// C. Cache 60 detik: untuk data yang berubah pelan, mis. daftar artikel.
export async function getPosts() {
const res = await fetch("https://api.example.com/posts", {
next: { revalidate: 60 },
});
return res.json();
}Contoh kedua: strategi per halaman. Halaman arsip blog memakai revalidate: 3600 (cukup segar tiap jam), sedangkan halaman dashboard admin memakai cache: "no-store" karena admin harus melihat angka terkini. Kuncinya: pilih strategi per data, bukan per aplikasi. Satu aplikasi boleh (dan sebaiknya) mencampur ketiganya.
// app/arsip/page.tsx
export default async function Arsip() {
const res = await fetch("https://api.example.com/posts", {
next: { revalidate: 3600 }, // arsip: segar tiap jam sudah cukup
});
const posts = await res.json();
return <ArchiveList posts={posts} />;
}Tabel keputusan cepat
| Kebutuhan | Opsi |
|---|---|
| Data nyaris statis (daftar negara) | default (cache selamanya) |
| Data berubah tiap menit/jam (artikel) | next: { revalidate: 60 } |
| Data real-time (stok, harga live) | cache: "no-store" |
Catatan teknis: Kebingungan nomor satu pemula adalah "kok dataku tidak update?", dan 90% penyebabnya adalah Data Cache. Saat debugging, langkah pertama: tambahkan
cache: "no-store"sementara. Kalau data langsung segar, berarti memang cache pelakunya, bukan API-mu yang rusak. Setelah yakin, kembalikan ke strategi yang tepat.
Tantangan
Bukti cache
Buat halaman fetch waktu server (new Date().toISOString() via API route). Refresh berkali-kali dan amati: dengan cache default waktu tidak berubah; dengan no-store berubah tiap refresh.