Nextjs Caching Revalidation Server Actions Mutations
Selesai membaca dokumentasi ini?
Kembali ke rujukan utama Next.js untuk melanjutkan topik lainnya.
Kembali ke rujukan utama Next.js untuk melanjutkan topik lainnya.
Di App Router, data tidak cuma "diambil"; data juga bisa di-cache, direvalidate, lalu diubah lewat action di server. Tiga hal ini saling terkait:
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.
Topik ini penting saat aplikasi Next.js mulai punya:
Kalau aplikasi hanya menampilkan halaman statis sederhana, detail cache dan revalidation tetap berlaku, tetapi dampaknya belum terlalu terasa.
Ada dua lapisan yang sering dibahas bersamaan, dan penting untuk tidak mencampurnya:
fetch di serverUntuk artikel ini, fokus utamanya adalah cache data dan bagaimana hasilnya memengaruhi render.
Secara umum, data bisa masuk cache ketika:
fetch memakai mode caching default yang diizinkan Next.jsContoh pola yang sering dipakai:
async function getPosts() {
const res = await fetch('https://example.com/api/posts', {
next: { revalidate: 60 },
})
return res.json()
}Maknanya:
Data cenderung tidak di-cache atau sengaja dibuat dinamis kalau:
fetch diberi konfigurasi yang memaksa fresh dataContoh 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 adalah proses menentukan kapan cache perlu diganti dengan data baru.
Ada dua pola yang paling umum:
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:
Cache dibersihkan atau ditandai ulang secara eksplisit setelah ada perubahan data.
Ini biasanya dipakai setelah mutation, misalnya:
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')
}Revalidation perlu dipikirkan saat:
Kalau tidak ada revalidation, aplikasi bisa terasa "benar" di server tetapi masih menampilkan data lama di UI.
Server Actions adalah fungsi server yang dipanggil langsung dari form atau interaksi client untuk menjalankan perubahan data di server.
Karakter utamanya:
<form action={...}>'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:
Mutation adalah momen ketika data berubah. Di Next.js, perubahan ini sebaiknya dipandang sebagai alur lengkap, bukan hanya panggilan fungsi.
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.
Aturan praktisnya sederhana:
Contoh:
/posts/posts/[slug] dan daftar postKalau scope revalidation terlalu luas, aplikasi masih benar tapi jadi lebih mahal. Kalau terlalu sempit, UI bisa menampilkan data yang kelihatan basi.
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.
Ini langkah utama. Tanpa revalidation, data server-rendered bisa terus mengarah ke cache lama.
Untuk client component, kadang UI perlu dipaksa membaca ulang data setelah mutation.
Pola yang umum:
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:
Jangan menyamakan semua perubahan UI dengan perubahan data server.
Kalau keduanya dicampur, UI mudah terasa "loncat" atau tidak konsisten.
'use server'
import { revalidatePath } from 'next/cache'
export async function createComment(formData: FormData) {
const postId = String(formData.get('postId') ?? '')
Pola ini bagus karena:
Diagram ini membantu melihat bahwa mutation bukan sekadar write operation. Ada fase baca, tulis, lalu sinkronisasi ulang.
revalidate kalau data aman dipakai ulang.cache: 'no-store' untuk data yang harus selalu fresh.revalidatePath() atau revalidateTag() setelah mutation yang mengubah data bersama.no-store padahal sebagian besar data bisa di-cache.Pilih pendekatan ini kalau:
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.