# Server Actions Next.js 16 di 2026: Mutasi, Revalidasi, dan Pertanyaan Wawancara
> Cara Server Actions Next.js 16 menangani mutasi, revalidasi, status pending, UI optimistis, dan keamanan, lengkap dengan pertanyaan wawancara yang menguji setiap konsep.
- Published: 2026-06-24
- Updated: 2026-07-06
- Author: SharpSkill
- Tags: next.js, server-actions, react, mutations, revalidation, interview
- Reading time: 11 min
---
Server Actions di Next.js mengubah fungsi async biasa menjadi endpoint mutasi sisi server yang dapat dipanggil langsung oleh sebuah form, tanpa route API, tanpa `fetch` di sisi klien, dan tanpa serialisasi JSON manual. Pada Next.js 16, Server Actions menjadi cara default untuk menulis mutasi data, dan hampir selalu muncul di setiap wawancara React senior pada 2026. Pembahasan mendalam ini menguraikan cara Server Actions dieksekusi, cara revalidasi menyebarkan data terbaru ke UI, cara memodelkan status pending dan error, serta aturan keamanan yang kerap menjebak banyak pengembang.
> **Definisi dalam Satu Kalimat**
>
> Server Action adalah fungsi async yang ditandai dengan `"use server"` yang hanya berjalan di server dan dapat dipanggil dari komponen klien atau form seolah-olah bersifat lokal, sementara Next.js menangani permintaan jaringan, serialisasi argumen, dan proteksi CSRF secara otomatis.
## Cara Server Actions Dieksekusi di Balik Layar
Server Action adalah fungsi khusus server yang dipanggil melalui jaringan lewat endpoint yang dihasilkan compiler. Ketika sebuah fungsi membawa direktif `"use server"`, compiler Next.js tidak pernah mengirim badan fungsi tersebut ke browser. Sebagai gantinya, compiler menggantinya dengan referensi ringan: sebuah ID action ter-hash yang dipetakan ke route POST yang dihasilkan. Memanggil action dari klien mengirim permintaan ke route tersebut, fungsi asli berjalan di server, dan hasil yang telah diserialisasi dialirkan kembali ke pemanggil.
Desain ini membawa tiga konsekuensi yang perlu diingat, baik untuk kode produksi maupun wawancara. Pertama, argumen dan nilai kembalian harus dapat diserialisasi, karena keduanya melintasi jaringan sebagai payload terenkode. Kedua, rahasia tetap berada di server, sebab badan fungsi tidak pernah di-bundle untuk klien. Ketiga, action yang dilekatkan pada sebuah `
)
}
```
Atribut `name` pada setiap field menjadi kunci yang dibaca oleh `formData.get()`. [Referensi FormData di MDN](https://developer.mozilla.org/en-US/docs/Web/API/FormData) menjelaskan cara browser menyusun objek ini dari form. Untuk kontrol yang lebih kaya, `next/form` memperluas elemen native dengan navigasi sisi klien dan prefetching sambil tetap mempertahankan kontrak berbasis action yang sama.
## Mengelola Status Pending dan Error dengan useActionState
Form nyata membutuhkan umpan balik validasi dan indikator loading. Hook React 19 `useActionState` membungkus sebuah Server Action, mengalirkan sebuah nilai state melalui setiap submission, dan mengekspos flag `isPending`. Hook ini menggantikan penamaan lama `useFormState` dan mengembalikan tuple berisi state saat ini, action yang telah dibungkus, dan boolean pending.
```tsx
// app/posts/new-post-form.tsx
"use client"
import { useActionState } from "react"
import { createPost } from "./actions"
const initialState = { error: "" }
export default function NewPostForm() {
// state carries the last return value; isPending tracks the in-flight request
const [state, formAction, isPending] = useActionState(createPost, initialState)
return (
)
}
```
Ketika sebuah hook menggerakkan action, tanda tangan fungsi mendapatkan parameter pertama `prevState` sebelum `FormData`. Mengembalikan sebuah objek alih-alih melempar error memungkinkan komponen merender validasi inline tanpa error boundary:
```typescript
// app/posts/actions.ts
"use server"
import { revalidateTag } from "next/cache"
import { PostService } from "@/lib/services/post-service"
type FormState = { error: string }
export async function createPost(
prevState: FormState,
formData: FormData,
): Promise {
const title = formData.get("title") as string
// Validate on the server; client validation is never trustworthy alone
if (!title || title.length < 3) {
return { error: "Title must be at least 3 characters." }
}
await PostService.create({ title })
// Invalidate by tag rather than path for finer-grained control
revalidateTag("posts")
return { error: "" }
}
```
[Referensi useActionState di react.dev](https://react.dev/reference/react/useActionState) membahas pola lanjutan seperti dukungan permalink untuk submission sebelum hidrasi. Mengembalikan state terstruktur menjaga jalur sukses dan jalur error tetap berada di satu tempat yang mudah diprediksi.
## revalidatePath vs revalidateTag: Memilih Invalidasi yang Tepat
Sebuah mutasi hanyalah separuh pekerjaan; UI harus mencerminkannya. Next.js menyediakan dua fungsi invalidasi dari `next/cache`, dan memilih yang benar sering menjadi pembeda dalam wawancara.
| Fungsi | Menginvalidasi | Paling cocok saat |
|----------|-------------|-----------|
| `revalidatePath("/posts")` | Setiap entri cache untuk sebuah route | Mutasi memengaruhi satu halaman atau layout yang jelas |
| `revalidateTag("posts")` | Setiap fetch ter-cache yang diberi tag tersebut | Data yang sama muncul di beberapa route |
`revalidatePath` bersifat kasar dan berpusat pada route: ia membersihkan cache untuk sebuah URL beserta pohon layout-nya. `revalidateTag` bersifat granular dan berpusat pada data: setiap `fetch` atau fungsi ter-cache yang diberi tag string tersebut akan dibuang di mana pun ia berada. Tag menskala lebih baik pada aplikasi besar karena satu mutasi dapat menyegarkan sidebar, listing, dan halaman detail dalam satu panggilan. Kedua fungsi menandai data sebagai basi alih-alih langsung mengambil ulang, sehingga permintaan berikutnya yang meregenerasi konten.
Dengan Cache Components di Next.js 16, pemberian tag terintegrasi dengan direktif `"use cache"` melalui `cacheTag`, yang mengubah cara caching dipahami di seluruh aplikasi. Interaksi tersebut dibahas mendalam pada [panduan Cache Components Next.js 16](/blog/react-next/nextjs-16-cache-components-use-cache-ppr-interview-questions) di SharpSkill.
## Pembaruan UI Optimistis dengan useOptimistic
Perjalanan bolak-balik jaringan menambah latensi yang terasa. Hook `useOptimistic` merender hasil yang diharapkan secara instan, lalu merekonsiliasinya dengan respons server begitu action selesai. Hook ini menerima state saat ini dan sebuah reducer yang menghasilkan versi optimistisnya.
```tsx
// app/posts/like-button.tsx
"use client"
import { useOptimistic } from "react"
import { likePost } from "./actions"
export function LikeButton({ postId, likes }: { postId: string; likes: number }) {
// optimisticLikes updates before the server confirms
const [optimisticLikes, addOptimisticLike] = useOptimistic(
likes,
(current) => current + 1,
)
async function handleLike() {
addOptimisticLike(null) // update the UI immediately
await likePost(postId) // then run the real mutation
}
return (
)
}
```
Jika action gagal, React otomatis mengembalikan nilai optimistis ke state server yang terkonfirmasi, sehingga tidak perlu rollback manual. [Referensi useOptimistic di react.dev](https://react.dev/reference/react/useOptimistic) merinci cara siklus rekonsiliasi berinteraksi dengan transition. Pola inilah yang membuat form Server Action terasa seresponsif SPA yang sepenuhnya dirender di klien.
## Mengikat Argumen dan Memanggil Action dari Event Handler
Tidak setiap mutasi berasal dari submission form. Menghapus baris, mengalihkan sebuah pengaturan, atau menyusun ulang daftar sering kali dipicu dari klik tombol atau event lain. Sebuah Server Action adalah referensi fungsi biasa di sisi klien, sehingga dapat dipanggil dari handler mana pun, asalkan panggilan tersebut berjalan di dalam sebuah transition agar UI tetap responsif.
Argumen tambahan yang bukan field form dilekatkan dengan `bind`, yang menghasilkan sebuah action baru dengan nilai-nilai tersebut diletakkan di depan. Inilah cara idiomatik untuk mengoper sebuah ID bersama `FormData` yang dikirimkan.
```tsx
// app/posts/post-actions.tsx
"use client"
import { useTransition } from "react"
import { deletePost, updatePost } from "./actions"
export function PostActions({ postId }: { postId: string }) {
const [isPending, startTransition] = useTransition()
// Bind the id so the action receives it before FormData
const updateWithId = updatePost.bind(null, postId)
return (
{/* Event-handler invocation wrapped in a transition */}
)
}
```
Argumen yang di-bind dienkripsi seperti nilai closure lainnya, sehingga `postId` tidak dapat dirusak dalam perjalanan, meski action tetap harus memastikan bahwa pemanggil memang berhak bertindak atas record tersebut. Membungkus panggilan langsung di dalam `startTransition` membuat React mempertahankan interaktivitas antarmuka dan menampilkan status pending tanpa pembungkus form.
## Keamanan Server Actions: Perlakukan Setiap Action sebagai API Publik
Kesalahpahaman paling berbahaya adalah menganggap sebuah Server Action bersifat privat hanya karena ditulis berdampingan dengan kode server. Kenyataannya, begitu sebuah action digunakan, Next.js menghasilkan endpoint POST publik yang dapat dipanggil siapa pun dengan permintaan yang dirancang khusus. Framework menyediakan ID action yang aman dan menghapus action yang tidak terpakai saat `next build`, tetapi ia tidak mengotorisasi pemanggilnya.
> **Otorisasi di Dalam Setiap Action**
>
> Server Actions adalah endpoint HTTP yang dapat dijangkau. Pemeriksaan sesi di sebuah layout atau middleware tidak melindunginya, karena penyerang dapat melakukan POST ke action secara langsung. Verifikasi sesi dan izin pemanggil di dalam badan action sebelum menyentuh data apa pun.
```typescript
// app/admin/actions.ts
"use server"
import { auth } from "@/lib/auth"
import { headers } from "next/headers"
import { UserService } from "@/lib/services/user-service"
export async function deleteUser(userId: string) {
// Authenticate on every invocation, not in a wrapper component
const session = await auth.api.getSession({ headers: await headers() })
if (!session || session.user.role !== "admin") {
throw new Error("Unauthorized")
}
await UserService.delete(userId)
}
```
Dua aturan lain juga penting. Nilai closure yang ditangkap oleh action inline dienkripsi sebelum dikirim ke klien dan didekripsi di server, sehingga token yang tertangkap tidak bocor dalam bentuk teks biasa, tetapi mengandalkan hal itu tetap rapuh: jauhkan rahasia sepenuhnya dari closure. Dan setiap argumen yang datang dari klien adalah input tak tepercaya yang harus divalidasi, persis seperti body REST. [Panduan keamanan data Next.js](https://nextjs.org/docs/app/guides/data-security) memformalkan model taint di balik jaminan ini. Untuk latihan terstruktur mengenai tradeoff ini, [modul wawancara Server Actions Next.js](/technologies/react-next/interview-questions/nextjs-server-actions) di SharpSkill melatih persis pertanyaan yang diajukan panel perekrutan.
## Pertanyaan Wawancara Server Actions Next.js 16
Pertanyaan-pertanyaan ini berulang kali muncul dalam proses seleksi React senior dan full-stack pada 2026.
**Mengapa sebuah Server Action hanya boleh menerima dan mengembalikan nilai yang dapat diserialisasi?** Karena argumen dan hasil melintasi batas jaringan antara klien dan server sebagai payload terenkode. Fungsi, instance kelas, dan node DOM tidak dapat diserialisasi, sehingga mengopernya akan melempar error. `FormData`, objek biasa, array, dan primitif aman digunakan.
**Bagaimana progressive enhancement bekerja dengan Server Actions?** Ketika sebuah action diteruskan langsung ke prop `action` sebuah form, browser mengirim form secara native melalui permintaan POST bahkan sebelum React terhidrasi. Next.js mencegat dan menjalankan action di sisi server, sehingga form berfungsi tanpa JavaScript klien dan meningkat menjadi pengalaman berbasis klien setelah hidrasi.
**Apa perbedaan antara melempar dan mengembalikan error di dalam sebuah action?** Melempar akan merambat ke error boundary terdekat dan tepat untuk kegagalan tak terduga. Mengembalikan objek state terstruktur melalui `useActionState` tepat untuk error validasi yang memang diharapkan dan sebaiknya dirender inline tanpa melepas form dari DOM.
**Apakah memanggil revalidatePath langsung mengambil ulang data?** Tidak. Ia menandai entri cache sebagai basi sehingga permintaan berikutnya yang meregenerasinya. Respons saat ini tidak terpengaruh, itulah sebabnya redirect setelah mutasi mendarat pada konten yang baru saja dirender.
Untuk konteks rendering yang lebih luas tempat mutasi-mutasi ini berjalan, uraian [React 19 Server Components di produksi](/blog/react-next/react-19-server-components-production) berpasangan secara alami dengan pengetahuan Server Actions.
## Kesimpulan
Server Actions Next.js 16 menyatukan mutasi, revalidasi, dan progressive enhancement ke dalam satu primitif yang mengutamakan server. Poin-poin utamanya:
- Tandai mutasi dengan `"use server"` dan teruskan ke prop `action` sebuah form untuk mendapatkan penanganan jaringan dan progressive enhancement secara cuma-cuma.
- Kendalikan status pending dan validasi dengan `useActionState`, dengan mengembalikan objek terstruktur untuk error yang diharapkan alih-alih melempar.
- Gunakan `revalidatePath` ketika satu route berubah dan `revalidateTag` ketika data yang sama mencakup beberapa route.
- Manfaatkan `useOptimistic` untuk merender hasil yang diharapkan secara instan dan biarkan React merekonsiliasi atau melakukan rollback otomatis.
- Otorisasi dan validasi di dalam badan setiap action, karena setiap action yang terpakai adalah endpoint HTTP publik yang tidak dilindungi oleh middleware saja.
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/id/blog/react-next/nextjs-16-server-actions-mutations-revalidation-interview-questions