Sebagai developer Next.js, kita selalu mencari cara untuk menyederhanakan arsitektur aplikasi dan meningkatkan pengalaman pengembangan. Dulu, mengelola data mutation seperti submit form atau update data seringkali membutuhkan API layer terpisah, baik itu REST API, GraphQL, atau tRPC. Proses ini, meskipun efektif, bisa menambah kompleksitas dan overhead.
Nah, di Next.js 13.4, Server Actions hadir sebagai game changer. Fitur ini memungkinkan Anda menjalankan kode server-side secara langsung dari komponen React client-side Anda, menghilangkan kebutuhan akan API endpoint terpisah untuk banyak kasus penggunaan. Ini bukan hanya tentang efisiensi, tapi juga tentang mengkonsolidasi logika backend dan frontend di satu tempat, membuat aplikasi Anda lebih kohesif dan mudah di-maintain.
Mari kita selami lebih dalam bagaimana Server Actions bekerja, mengapa ini penting, dan bagaimana Anda bisa mengimplementasikannya untuk membangun aplikasi full-stack yang lebih powerful dan efisien.
Apa Itu Server Actions di Next.js?
Server Actions adalah kemampuan Next.js untuk menjalankan kode server-side langsung dari client component Anda, baik itu dengan onSubmit event dari sebuah form atau dengan memanggil fungsi secara langsung. Konsep dasarnya adalah Remote Procedure Call (RPC), di mana Anda bisa “memanggil” sebuah fungsi yang didefinisikan di server dari client.
Fungsi yang dideklarasikan sebagai Server Action akan dieksekusi di server dan bisa melakukan operasi apapun yang umumnya dilakukan di backend, seperti:
- Berinteraksi dengan database.
- Mengakses file system.
- Menangani autentikasi.
- Menggunakan variabel environment yang hanya ada di server.
Keuntungan utamanya adalah menyederhanakan alur kerja data mutation. Daripada membuat API route terpisah (/api/create-user, /api/update-post, dll.) dan kemudian memanggilnya dari client menggunakan fetch, Anda cukup mendefinisikan sebuah fungsi di server dan memanggilnya langsung.
Mengapa Server Actions Penting untuk Developer?
Server Actions menawarkan beberapa keuntungan signifikan yang mengubah cara kita membangun aplikasi Next.js:
1. Mengurangi Kompleksitas Arsitektur
Anda tidak perlu lagi membuat API route terpisah hanya untuk operasi data mutation. Ini sangat mengurangi boilerplate code dan jumlah file yang harus di-maintain.
2. Developer Experience (DX) yang Lebih Baik
Logika frontend dan backend untuk sebuah fitur kini bisa ditempatkan lebih dekat, terkadang bahkan dalam satu file komponen. Ini memudahkan pemahaman dan debugging, terutama untuk fitur yang relatif sederhana.
3. Performa Optimal
Next.js mengoptimalkan pengiriman kode Server Action. Hanya JavaScript yang diperlukan untuk memanggil fungsi yang dikirim ke browser, bukan seluruh logika server. Selain itu, Server Actions terintegrasi dengan data revalidation di Next.js, memastikan UI Anda selalu menampilkan data terbaru tanpa perlu refresh manual.
4. Keamanan yang Ditingkatkan
Karena Server Actions berjalan di server, mereka memiliki akses ke resource server-side yang aman (seperti kredensial database) tanpa mengeksposnya ke client. Namun, validasi input tetap krusial.
5. Integrasi Seamless dengan Form HTML
Anda bisa langsung mengaitkan Server Action dengan atribut action pada form HTML, memungkinkan progressive enhancement secara otomatis. Form akan berfungsi bahkan jika JavaScript dinonaktifkan.
Prasyarat Sebelum Memulai
Untuk mengikuti panduan ini, pastikan Anda memiliki:
- Node.js terinstal di sistem Anda.
- Pengetahuan dasar React dan Next.js (terutama App Router).
- Proyek Next.js yang menggunakan App Router (versi 13.4 atau lebih baru).
Mengaktifkan Server Actions di Next.js
Secara default, Server Actions sudah aktif jika Anda membuat proyek baru dengan App Router. Namun, jika Anda mengupgrade proyek lama atau ingin memastikan, Anda bisa mengaktifkannya secara eksplisit di file next.config.js:
/ @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
serverActions: true,
},
};
module.exports = nextConfig;
Setelah itu, restart server pengembangan Anda.
Dasar-dasar Menggunakan Server Actions
Ada dua cara utama untuk mendefinisikan Server Action:
1. Mendefinisikan Server Action di Dalam Server Component
Server Actions bisa didefinisikan langsung di dalam Server Component. Ini berguna ketika aksi tersebut sangat terikat dengan komponen tersebut.
// app/page.tsx
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
export default function Page() {
async function createTodo(formData: FormData) {
'use server'; // Directive ini WAJIB
const todo = formData.get('todo') as string;
// Logika untuk menyimpan 'todo' ke database
console.log('Menambahkan todo:', todo);
// Revalidasi cache data untuk path ini
revalidatePath('/');
// Bisa juga redirect setelah aksi
// redirect('/success-page');
}
return (
Daftar Todo
{/* Tampilkan daftar todo di sini */}
);
}
Perhatikan beberapa hal penting:
'use server';: Ini adalah directive ajaib yang memberitahu Next.js bahwa fungsi ini harus dieksekusi di server. Ini WAJIB ada di baris pertama fungsi atau di bagian atas file jika semua fungsi di file tersebut adalah Server Actions.formData: FormData: Jika Server Action dipanggil dari atributactionsebuah form, ia akan menerima objekFormDatasebagai argumen pertamanya. Ini sangat memudahkan untuk mengakses input dari form.revalidatePath/revalidateTag: Setelah melakukan mutasi data, Anda sering ingin UI client-side menampilkan data terbaru. Fungsi-fungsi ini darinext/cachememungkinkan Anda untuk me-revalidate cache data Next.js, sehingga data terbaru akan diambil dan UI diperbarui secara otomatis.redirect: Darinext/navigation, ini bisa digunakan untuk mengarahkan pengguna ke halaman lain setelah Server Action berhasil dieksekusi.
Untuk menjaga kode tetap rapi dan reusable, Anda bisa mendefinisikan Server Actions di file terpisah dan mengimpornya ke mana pun Anda butuhkan.
// lib/actions.ts atau app/actions.ts
'use server'; // Directive ini di bagian atas file menandakan semua export adalah Server Actions
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const content = formData.get('content') as string;
// Lakukan validasi input di sini
if (!title || title.length
// app/blog/page.tsx (Server Component)
import { createPost } from '@/lib/actions';
export default function BlogPage() {
return (
Buat Post Baru
{/* Tampilkan daftar post di sini */}
);
}
// app/profile/[[...id]]/page.tsx (Client Component)
'use client';
import { deleteUser } from '@/lib/actions';
export default function UserProfile({ params }: { params: { id: string[] } }) {
const userId = params.id ? params.id[0] : 'default-user-id';
const handleDelete = async () => {
if (confirm('Apakah Anda yakin ingin menghapus user ini?')) {
try {
await deleteUser(userId);
alert('User berhasil dihapus!');
// Mungkin redirect atau update UI lainnya
} catch (error: any) {
alert(`Gagal menghapus user: ${error.message}`);
}
}
};
return (
Profil Pengguna {userId}
);
}
Dalam contoh Client Component di atas, deleteUser dipanggil langsung sebagai fungsi async. Ini menunjukkan fleksibilitas Server Actions; mereka bisa dipanggil baik dari atribut action form maupun secara imperatif dari handler event JavaScript.
Menangani State Loading dan Error dengan Server Actions
Ketika berinteraksi dengan Server Actions dari Client Component, penting untuk memberikan feedback kepada user (misalnya, status loading atau pesan error). Next.js menyediakan beberapa hook untuk membantu hal ini:
1. useFormStatus
Hook ini memungkinkan Anda untuk membaca status pengiriman form di dalam Client Component. Ini sangat berguna untuk menonaktifkan tombol submit atau menampilkan spinner loading.
// app/components/SubmitButton.tsx
'use client';
import { useFormStatus } from 'react-dom'; // Ini dari 'react-dom', bukan 'next/navigation'
export function SubmitButton() {
const { pending } = useFormStatus();
return (
);
}
// app/page.tsx (Client Component)
'use client';
import { createTodo } from '@/lib/actions';
import { SubmitButton } from './components/SubmitButton';
export default function TodoForm() {
return (
);
}
Penting: SubmitButton harus menjadi children dari elemen <form> yang memanggil Server Action agar useFormStatus dapat berfungsi dengan benar.
2. useFormState
Hook ini memungkinkan Anda untuk mengelola state dari form yang dikirimkan, termasuk pesan error atau feedback lainnya. Ini mengembalikan array dengan state saat ini dan fungsi baru yang diperbarui untuk form action Anda.
// lib/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function createUser(prevState: any, formData: FormData) {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
if (!name || name.length
// app/users/create/page.tsx (Client Component)
'use client';
import { useFormState } from 'react-dom';
import { createUser } from '@/lib/actions';
import { SubmitButton } from '../../components/SubmitButton'; // Gunakan SubmitButton yang sudah ada
const initialState = {
message: '',
success: false,
};
export default function CreateUserForm() {
const [state, formAction] = useFormState(createUser, initialState);
return (
);
}
useFormState menerima dua argumen: fungsi Server Action Anda dan state awal. Ia mengembalikan state saat ini dan fungsi yang dapat Anda teruskan ke atribut action form Anda. Setiap kali form disubmit, fungsi Server Action akan dipanggil dengan state sebelumnya sebagai argumen pertama.
Keamanan dan Validasi di Server Actions
Meskipun Server Actions berjalan di server, validasi input tetap sangat penting. Jangan pernah percaya data yang datang dari client. Selalu lakukan validasi server-side untuk melindungi aplikasi Anda dari input yang berbahaya atau tidak valid.
Contoh validasi sederhana sudah terlihat di bagian createUser di atas. Untuk validasi yang lebih kompleks, Anda bisa menggunakan library seperti Zod.
// lib/actions.ts (dengan Zod)
'use server';
import { z } from 'zod';
import { revalidatePath } from 'next/cache';
const userSchema = z.object({
name: z.string().min(3, 'Nama harus minimal 3 karakter.'),
email: z.string().email('Email tidak valid.'),
});
export async function createValidatedUser(prevState: any, formData: FormData) {
const validatedFields = userSchema.safeParse({
name: formData.get('name'),
email: formData.get('email'),
});
if (!validatedFields.success) {
return {
message: 'Validasi gagal!',
errors: validatedFields.error.flatten().fieldErrors,
};
}
const { name, email } = validatedFields.data;
console.log('Membuat user valid:', { name, email });
// ... simpan ke database ...
revalidatePath('/users');
return { message: 'User berhasil dibuat!', success: true };
}
Selain validasi, pastikan Anda juga menerapkan:
- Autentikasi: Pastikan hanya user yang terautentikasi yang dapat melakukan aksi tertentu.
- Otorisasi: Pastikan user yang terautentikasi memiliki izin yang diperlukan untuk melakukan aksi tersebut.
Pengalaman dan Pertimbangan Praktis
Dalam praktik pengembangan nyata, Server Actions menawarkan banyak kemudahan, tetapi juga penting untuk memahami kapan dan bagaimana menggunakannya secara optimal.
Kapan Menggunakan Server Actions vs. API Routes?
Ini adalah pertanyaan umum yang sering muncul. Berikut adalah panduan yang sering saya pakai:
- Gunakan Server Actions jika:
- Anda melakukan operasi mutasi data (CUD - Create, Update, Delete) yang terikat erat dengan UI atau form di Next.js App Router Anda.
- Anda ingin menyederhanakan logika backend untuk fitur-fitur yang tidak memerlukan API publik atau yang sangat spesifik untuk aplikasi Next.js Anda.
- Anda membutuhkan integrasi seamless dengan revalidasi cache Next.js.
- Anda memprioritaskan developer experience dan ingin mengkonsolidasi kode server dan client di satu tempat.
- Gunakan API Routes (Route Handlers) jika:
- Anda membangun API publik atau internal yang akan diakses oleh client lain selain aplikasi Next.js Anda (misalnya, aplikasi mobile, third-party services, atau aplikasi frontend lain yang tidak berbasis Next.js).
- Anda membutuhkan kontrol penuh atas headers HTTP, status code, dan respons yang lebih kompleks (meskipun Server Actions juga bisa mengembalikan data).
- Anda ingin memisahkan secara ketat logika API dari logika komponen UI Anda, terutama untuk aplikasi berskala besar dengan banyak konsumen API.
- Anda mengimplementasikan middleware kustom yang kompleks sebelum memproses request.
Secara umum, untuk sebagian besar operasi form submission dan data mutation di aplikasi Next.js, Server Actions adalah pilihan yang sangat efisien. Namun, jika Anda membangun API sejati, Route Handlers (API Routes) masih menjadi jawabannya.
Testing Server Actions
Menguji Server Actions sedikit berbeda dari menguji API Routes. Karena mereka adalah fungsi JavaScript biasa yang dieksekusi di server, Anda bisa mengujinya seperti fungsi server-side lainnya menggunakan Jest, Vitest, atau tools testing lainnya. Anda mungkin perlu memock interaksi dengan database atau layanan eksternal lainnya.
Batasan dan Pertimbangan Lain
- Ukuran Payload: Ada batasan ukuran payload yang bisa dikirimkan ke Server Action. Untuk file upload yang sangat besar, Anda mungkin perlu memikirkan solusi streaming atau menggunakan layanan penyimpanan cloud langsung dari client.
- Kompilasi: Server Actions dikompilasi ke dalam bundle JavaScript yang dieksekusi di server, jadi pastikan tidak ada efek samping yang tidak diinginkan dari kode server yang Anda tulis.
- Global State: Server Actions dieksekusi dalam konteks request yang terisolasi. Jangan mengandalkan global state yang akan persisten di antara request.
Contoh Integrasi dengan Database (Mental Model)
Dalam praktiknya, Server Actions akan berinteraksi dengan database Anda. Misalnya, menggunakan ORM seperti Prisma:
// lib/db.ts
import { PrismaClient } from '@prisma/client';
let prisma: PrismaClient;
if (process.env.NODE_ENV === 'production') {
prisma = new PrismaClient();
} else {
// Hindari instansi PrismaClient baru di hot-reloading development
if (!(global as any).prisma) {
(global as any).prisma = new PrismaClient();
}
prisma = (global as any).prisma;
}
export default prisma;
// lib/actions.ts (contoh dengan Prisma)
'use server';
import { revalidatePath } from 'next/cache';
import prisma from '@/lib/db'; // Import Prisma client Anda
export async function createProduct(formData: FormData) {
const name = formData.get('name') as string;
const price = parseFloat(formData.get('price') as string);
if (!name || price
Dengan cara ini, Server Action Anda secara aman berkomunikasi dengan database melalui ORM.
Masalah yang Sering Terjadi
Saat pertama kali menggunakan Server Actions, beberapa masalah umum mungkin muncul. Berikut adalah beberapa di antaranya dan cara mengatasinya:
1. Lupa Directive 'use server'
Gejala: Fungsi Anda tidak berjalan di server dan mungkin menimbulkan error runtime terkait akses ke variabel environment server-side atau operasi I/O file system.
Penyebab: Anda lupa menambahkan 'use server' di baris pertama fungsi Server Action atau di bagian atas file yang berisi beberapa Server Action.
Solusi: Tambahkan 'use server';. Pastikan itu adalah baris pertama kode di dalam fungsi atau di bagian paling atas file jika Anda mendefinisikan Server Action di file terpisah.
2. Meneruskan Objek Non-Serializable dari Client Component
Gejala: Error serialization di console browser atau server, seperti "Only plain objects, and a few built-ins, can be passed to Server Actions."
Penyebab: Saat memanggil Server Action secara imperatif dari Client Component (misalnya, di handler onClick), Anda mencoba meneruskan objek JavaScript yang kompleks (seperti instance class, fungsi, atau objek yang berisi siklus referensi) sebagai argumen.
Solusi: Pastikan semua argumen yang Anda teruskan ke Server Action dari client adalah tipe data yang serializable (string, number, boolean, array, plain object, FormData). Jika Anda perlu meneruskan data kompleks, ubah menjadi representasi serializable terlebih dahulu.
3. Revalidasi Cache Tidak Bekerja
Gejala: Setelah Server Action berhasil dieksekusi, UI tidak diperbarui untuk menampilkan data terbaru.
Penyebab: Anda lupa memanggil revalidatePath('/your-path') atau revalidateTag('your-tag') di dalam Server Action Anda, atau path/tag yang Anda revalidasi tidak cocok dengan data yang ingin Anda perbarui.
Solusi: Pastikan Anda memanggil fungsi revalidasi yang sesuai setelah operasi mutasi data berhasil. Periksa apakah path atau tag yang Anda gunakan benar-benar mencakup data yang perlu diperbarui.
4. Masalah Keamanan: Tidak Melakukan Validasi Input
Gejala: Data yang tidak valid atau berbahaya tersimpan di database, atau Server Action dieksekusi oleh user yang tidak berhak.
Penyebab: Anda mengandalkan validasi di sisi client saja atau tidak melakukan otorisasi yang memadai.
Solusi: Selalu lakukan validasi input di sisi server, meskipun Anda sudah memvalidasinya di client. Gunakan library seperti Zod. Implementasikan juga logika autentikasi dan otorisasi yang ketat di Server Action Anda untuk memastikan hanya user yang berhak yang bisa melakukan aksi tertentu.
5. Salah Membedakan antara Server Components dan Server Actions
Gejala: Kebingungan tentang kapan menggunakan 'use client' atau 'use server', atau mencoba melakukan operasi server-side langsung di Client Component.
Penyebab: Kurangnya pemahaman tentang perbedaan fundamental antara Server Components (yang secara default dieksekusi di server untuk rendering UI) dan Server Actions (fungsi yang dieksekusi di server untuk data mutation).
Solusi: Ingat bahwa Server Components adalah tentang rendering UI di server, sedangkan Server Actions adalah tentang menjalankan fungsi di server. Client Components menggunakan 'use client' untuk memungkinkan interaktivitas, dan Server Actions dapat dipanggil dari Server Components maupun Client Components (dengan pengecualian serialisasi).
FAQ
Apa perbedaan utama antara Server Actions dan Route Handlers (API Routes)?
Server Actions dirancang khusus untuk operasi data mutation yang dipanggil dari komponen React Anda, seringkali terkait dengan form submission, dan terintegrasi erat dengan sistem cache dan revalidation Next.js. Route Handlers (API Routes) lebih mirip API endpoint tradisional; mereka ideal untuk membangun API publik, mengontrol headers HTTP, dan melayani berbagai jenis client.
Bisakah Server Actions mengembalikan data?
Ya, Server Actions dapat mengembalikan data. Anda bisa menangani nilai kembalian ini di sisi client, terutama jika Anda memanggil Server Action secara imperatif (misalnya, dengan onClick) atau menggunakan hook useFormState untuk menerima feedback setelah form disubmit.
Apakah Server Actions aman secara default?
Server Actions berjalan di server, yang berarti mereka memiliki akses ke environment server-side yang aman. Namun, ini tidak secara otomatis membuat aksi itu sendiri aman. Anda harus tetap melakukan validasi input yang ketat, autentikasi, dan otorisasi di dalam Server Action untuk mencegah kerentanan keamanan seperti injeksi SQL, XSS, atau akses data yang tidak sah.
Bagaimana cara menangani upload file dengan Server Actions?
Saat form dengan input type="file" disubmit ke Server Action, file akan tersedia di objek FormData sebagai instance File. Anda kemudian bisa memproses file tersebut di server, misalnya menyimpannya ke disk atau mengunggahnya ke layanan penyimpanan cloud (seperti AWS S3 atau Cloudinary).
Apakah Server Actions kompatibel dengan database tertentu?
Server Actions agnostik terhadap jenis database atau ORM yang Anda gunakan. Karena mereka adalah kode server-side, Anda bisa menggunakan driver database atau ORM favorit Anda (Prisma, Drizzle, Sequelize, Knex.js, atau bahkan driver database langsung) sama seperti Anda akan menggunakannya di API Route atau aplikasi backend Node.js lainnya.
Kesimpulan
Server Actions adalah salah satu fitur paling revolusioner di Next.js App Router yang benar-benar mengubah cara kita berpikir tentang full-stack development. Dengan memungkinkan Anda menjalankan kode server-side langsung dari komponen client, Server Actions menyederhanakan arsitektur, meningkatkan developer experience, dan mengoptimalkan performa aplikasi Anda.
Meskipun ada kurva pembelajaran kecil, terutama dalam memahami konteks server dan client, manfaat yang ditawarkan Server Actions sangat besar. Saya sangat merekomendasikan setiap developer Next.js untuk menguasai fitur ini. Dengan pemahaman yang kuat tentang Server Actions, Anda dapat membangun aplikasi yang lebih ramping, lebih cepat, dan lebih mudah di-maintain, membawa pengalaman coding Anda ke level berikutnya.
Selamat mencoba dan semoga sukses dalam membangun aplikasi Next.js Anda!
TAGS: Next.js, Server Actions, React, Full-stack Development, App Router, Web Development, Data Mutation, Form Handling, Programming Tutorial


