Nextjs Caching Revalidation Server Actions Mutations
Definisi
Di App Router, data tidak cuma "diambil"; data juga bisa di-cache, direvalidate, lalu diubah lewat action di server. Tiga hal ini saling terkait:
- caching menentukan kapan hasil fetch boleh dipakai ulang
- revalidation menentukan kapan cache dianggap kedaluwarsa dan perlu dibuat ulang
- server actions memproses perubahan data dari form atau interaksi UI di server
Kalau alurnya dipahami sebagai satu sistem, kita bisa menjelaskan kenapa sebuah halaman kadang cepat, kadang menunggu fetch ulang, dan kadang harus disinkronkan lagi setelah user submit form.
Kapan dipakai
Topik ini penting saat aplikasi Next.js mulai punya:
- data yang sering dibaca ulang oleh banyak user
- halaman yang perlu terasa cepat tanpa fetch berulang di setiap request
- form yang mengubah data lalu harus memperbarui UI segera setelah submit
- kombinasi data statis, data semi-dinamis, dan data benar-benar real-time
- kebutuhan untuk menjaga UI server-rendered tetap sinkron dengan state terbaru di server
Kalau aplikasi hanya menampilkan halaman statis sederhana, detail cache dan revalidation tetap berlaku, tetapi dampaknya belum terlalu terasa.
Cara kerja caching di Next.js
Ada dua lapisan yang sering dibahas bersamaan, dan penting untuk tidak mencampurnya:
- cache hasil
fetchdi server - cache hasil render / route segment
Untuk artikel ini, fokus utamanya adalah cache data dan bagaimana hasilnya memengaruhi render.
Kapan data di-cache
Secara umum, data bisa masuk cache ketika:
- request dilakukan dari Server Component
fetchmemakai mode caching default yang diizinkan Next.js- halaman tidak dipaksa selalu dinamis
- tidak ada konfigurasi yang menyatakan data harus selalu fresh
Contoh pola yang sering dipakai:
async function getPosts() {
const res = await fetch('https://example.com/api/posts', {
next: { revalidate: 60 },
})
return res.json()
}Maknanya:
- hasil fetch boleh dipakai ulang selama 60 detik
- setelah itu Next.js boleh menganggap data lama dan meminta pembaruan
- request berikutnya tidak selalu membebani sumber data utama
Kapan data tidak di-cache
Data cenderung tidak di-cache atau sengaja dibuat dinamis kalau:
fetchdiberi konfigurasi yang memaksa fresh data- route memang harus selalu terbaru, misalnya dashboard yang sangat sensitif
- data bergantung pada cookies, headers, atau konteks request tertentu
- developer memilih pendekatan yang memprioritaskan konsistensi penuh daripada kecepatan
Contoh pola yang cenderung selalu fresh:
await fetch('https://example.com/api/me', {
cache: 'no-store',
})Pola seperti ini cocok untuk data personal atau data yang tidak boleh basi, tapi konsekuensinya cache tidak membantu banyak.
Revalidation
Revalidation adalah proses menentukan kapan cache perlu diganti dengan data baru.
Ada dua pola yang paling umum:
1. Time-based revalidation
Cache dianggap valid selama durasi tertentu, lalu diperbarui saat ada request setelah durasi itu lewat.
await fetch('https://example.com/api/posts', {
next: { revalidate: 300 },
})Artinya:
- data bisa dipakai ulang selama 5 menit
- setelah 5 menit, Next.js boleh mengambil data baru
- hasilnya cocok untuk konten yang tidak berubah setiap detik
2. On-demand revalidation
Cache dibersihkan atau ditandai ulang secara eksplisit setelah ada perubahan data.
Ini biasanya dipakai setelah mutation, misalnya:
- user membuat post baru
- user mengedit profil
- admin mengubah status pesanan
- form submit mengubah data yang juga dipakai di halaman lain
Di Next.js App Router, on-demand revalidation biasanya dilakukan dengan revalidatePath() atau revalidateTag().
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
// simpan data ke database
revalidatePath('/posts')
}Kapan revalidation diperlukan
Revalidation perlu dipikirkan saat:
- data yang ditampilkan berasal dari cache
- data itu bisa berubah karena aksi user lain atau aksi yang baru saja dilakukan
- halaman lain memakai sumber data yang sama
- UI harus menampilkan state terbaru setelah mutation berhasil
Kalau tidak ada revalidation, aplikasi bisa terasa "benar" di server tetapi masih menampilkan data lama di UI.
Server Actions
Server Actions adalah fungsi server yang dipanggil langsung dari form atau interaksi client untuk menjalankan perubahan data di server.
Karakter utamanya:
- dieksekusi di server
- aman untuk operasi seperti insert, update, delete
- bisa dipanggil dari
<form action={...}> - sering dipasangkan dengan revalidation agar UI tetap sinkron
Alur dasar
'use server'
export async function saveProfile(formData: FormData) {
const name = String(formData.get('name') ?? '')
// simpan ke database
}Lalu di form:
<form action={saveProfile}>
<input name="name" />
<button type="submit">Simpan</button>
</form>Saat user submit:
- browser mengirim form
- Server Action dipanggil di server
- data disimpan atau diubah
- cache yang relevan direvalidate
- UI dirender ulang atau di-refresh agar membaca data terbaru
Mutations flow
Mutation adalah momen ketika data berubah. Di Next.js, perubahan ini sebaiknya dipandang sebagai alur lengkap, bukan hanya panggilan fungsi.
Alur umum mutation
Diagram ini menunjukkan bahwa mutation yang baik tidak berhenti di "data tersimpan". Setelah itu masih ada langkah penting: cache lama harus disesuaikan supaya UI tidak tertinggal.
Kapan data direvalidate setelah mutation
Aturan praktisnya sederhana:
- kalau data yang berubah dipakai ulang di halaman yang sama, revalidate halaman itu
- kalau data yang berubah dipakai di banyak route, revalidate semua path atau tag yang relevan
- kalau perubahan hanya berdampak lokal dan tidak ada data cache yang bergantung, revalidation bisa lebih sempit
Contoh:
- membuat post baru → revalidate
/posts - mengubah judul post → revalidate
/posts/[slug]dan daftar post - memperbarui profil user → revalidate halaman profil dan header yang menampilkan nama user
Kalau scope revalidation terlalu luas, aplikasi masih benar tapi jadi lebih mahal. Kalau terlalu sempit, UI bisa menampilkan data yang kelihatan basi.
Menjaga UI tetap sinkron
Sinkron itu artinya UI yang dilihat user sejalan dengan sumber data terbaru di server. Di Next.js App Router, ada beberapa teknik yang sering dipakai bersama.
1. Revalidate sumber data yang tepat
Ini langkah utama. Tanpa revalidation, data server-rendered bisa terus mengarah ke cache lama.
2. Refetch atau refresh setelah action berhasil
Untuk client component, kadang UI perlu dipaksa membaca ulang data setelah mutation.
Pola yang umum:
- tampilkan optimistik state sementara
- jalankan server action
- setelah sukses, refresh data server atau query client
- sembunyikan loading state
3. Gunakan optimistic UI dengan hati-hati
Optimistic UI berarti UI diperbarui dulu seolah-olah mutation berhasil, lalu disesuaikan setelah server mengonfirmasi.
Ini bagus untuk pengalaman cepat, tapi harus disiapkan dengan baik:
- ada state pending yang jelas
- ada rollback kalau server menolak perubahan
- validasi tetap dilakukan di server, bukan hanya di client
4. Pisahkan data cache dan state lokal
Jangan menyamakan semua perubahan UI dengan perubahan data server.
- state lokal: misalnya input form, modal, tab aktif
- state server: misalnya daftar post, profil user, status transaksi
Kalau keduanya dicampur, UI mudah terasa "loncat" atau tidak konsisten.
Contoh pola yang sehat
'use server'
import { revalidatePath } from 'next/cache'
export async function createComment(formData: FormData) {
const postId = String(formData.get('postId') ?? '')
const content = String(formData.get('content') ?? '').trim()
if (!content) {
throw new Error('Komentar tidak boleh kosong')
}
// simpan komentar ke database
revalidatePath(`/posts/${postId}`)
revalidatePath('/posts')
}Pola ini bagus karena:
- validasi tetap dilakukan di server
- mutation disimpan di satu tempat
- cache halaman detail dan daftar ikut diperbarui
- UI berikutnya membaca data yang sama dari sumber yang baru
Alur baca, ubah, lalu sinkron
Diagram ini membantu melihat bahwa mutation bukan sekadar write operation. Ada fase baca, tulis, lalu sinkronisasi ulang.
Catatan implementasi
- Gunakan cache default atau
revalidatekalau data aman dipakai ulang. - Gunakan
cache: 'no-store'untuk data yang harus selalu fresh. - Gunakan
revalidatePath()ataurevalidateTag()setelah mutation yang mengubah data bersama. - Letakkan validasi penting di Server Action, bukan hanya di client.
- Jangan mengandalkan optimistic UI tanpa rollback atau pending state.
- Pastikan halaman yang menampilkan data turunan ikut direvalidate, bukan hanya halaman form-nya.
- Kalau satu mutation memengaruhi banyak tampilan, pikirkan skema tag agar revalidation lebih presisi.
Jebakan umum
- Menganggap submit form otomatis memperbarui semua cache.
- Merevalidate halaman yang salah sehingga UI tetap menampilkan data lama.
- Menaruh validasi utama hanya di browser.
- Membuat semua fetch
no-storepadahal sebagian besar data bisa di-cache. - Lupa memisahkan state input lokal dengan data server.
- Menggunakan optimistic update tanpa memastikan server benar-benar sukses.
Ringkasan keputusan
Pilih pendekatan ini kalau:
- cache → data sering dibaca dan tidak perlu selalu fresh
- revalidation → data berubah dan harus disegarkan setelah waktu tertentu atau setelah mutation
- Server Actions → ada form atau aksi yang mengubah data di server
- mutation flow → ingin UI tetap sinkron setelah perubahan data terjadi
Kalau diringkas sekali lagi: cache mempercepat baca, revalidation menjaga akurasi, Server Actions menangani tulis, dan sinkronisasi UI memastikan user tidak melihat data yang sudah ketinggalan.