
Khi React lần đầu tiên tạo ra một cuộc cách mạng trong hệ sinh thái frontend, ranh giới giữa logic phía client và việc tìm nạp dữ liệu phía server là một ranh giới nghiêm ngặt. Qua nhiều năm, Next.js đã dần thu hẹp khoảng cách này - đầu tiên với getServerSideProps và getStaticProps, sau đó là với App Router và Server Components. Nhưng còn các thao tác thay đổi dữ liệu (mutations) thì sao? Làm thế nào để chúng ta gửi dữ liệu từ client trở lại server một cách liền mạch mà không gặp phải rắc rối khi xây dựng các endpoint API tùy chỉnh?
Hãy cùng tìm hiểu về Server Actions. Được giới thiệu dưới dạng tính năng alpha trong Next.js 13 và được ổn định trong Next.js 14, Server Actions đã thay đổi hoàn toàn các quy tắc về thao tác thay đổi dữ liệu full-stack trong các ứng dụng React. Trong bài phân tích chuyên sâu này, chúng ta sẽ khám phá cơ chế hoạt động của Server Actions, các vấn đề bảo mật liên quan và cách chúng tương tác với bộ nhớ đệm, cải tiến lũy tiến (progressive enhancement) và các hook nguyên thủy đang phát triển của React.
Trước Server Actions, một biểu mẫu gửi dữ liệu điển hình trong ứng dụng React yêu cầu một dàn nhạc gồm các phần liên kết với nhau:
useState hoặc các thư viện biểu mẫu phức tạp như react-hook-form./api/submit) để nhận và xử lý payload.fetch hoặc một thư viện tìm nạp dữ liệu (như React Query hoặc SWR) để truyền payload, xử lý trạng thái tải và phân tích phản hồi.Mặc dù có chức năng, cách tiếp cận này đã tạo ra một lượng lớn mã lặp lại (boilerplate) và các thác nước mạng (network waterfalls). Nó cũng buộc các nhà phát triển phải kết nối chặt chẽ giữa các thành phần frontend và các API route backend.
Server Action, về cơ bản, là một hàm bất đồng bộ được định nghĩa trên server có thể được gọi trực tiếp từ các client hoặc server component. Khi bạn định nghĩa một hàm với chỉ thị "use server", Next.js thực hiện một bước biên dịch để thay đổi cơ bản cách hàm đó được hiển thị.
// actions.ts
"use server";
import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
export async function createPost(formData: FormData) {
const title = formData.get("title");
await db.post.create({
data: { title: title as string }
});
revalidatePath("/posts");
}
Khi bạn import và gọi hàm này từ một Client Component, Next.js không đóng gói mã của hàm vào client bundle. Thay vào đó, bundler tạo ra một cơ chế gọi thủ tục từ xa (RPC). Client bundle nhận được một hàm proxy. Khi proxy này được thực thi, nó gửi một yêu cầu HTTP POST đến server Next.js chứa định danh của action và các đối số của nó.
Server giải mã payload, thực thi hàm gốc một cách an toàn và trả về kết quả (cùng với bất kỳ cập nhật giao diện người dùng hoặc hướng dẫn xác thực lại nào). Toàn bộ quá trình này bỏ qua nhu cầu về một API route rõ ràng, giảm đáng kể mã lặp lại và gánh nặng nhận thức.
Một trong những khía cạnh mạnh mẽ nhất của Server Actions là sự tích hợp sâu sắc của chúng với bộ nhớ đệm của Next.js App Router. Trong các thao tác thay đổi dữ liệu REST hoặc GraphQL truyền thống, việc cập nhật dữ liệu trên server có nghĩa là bạn phải tự động vô hiệu hóa bộ nhớ đệm phía client (ví dụ: sử dụng invalidateQueries của React Query).
Với Server Actions, việc vô hiệu hóa bộ nhớ đệm được tích hợp vào framework thông qua revalidatePath và revalidateTag. Khi một action hoàn tất, Next.js có thể phản hồi bằng một lệnh cho client router để tìm nạp lại các đường dẫn cụ thể. Điều này xảy ra trong một vòng lặp mạng duy nhất.
Ví dụ:
export async function updateProfile(data: UserData) {
"use server"
await updateDb(data);
revalidateTag("user-profile");
}
Khi thực thi, Next.js sẽ tự động xóa bộ nhớ đệm phía server cho thẻ user-profile và hướng dẫn client router áp dụng payload Server Component đã cập nhật một cách liền mạch, bảo toàn trạng thái phía client nếu có thể.
Một quan niệm sai lầm phổ biến là Server Actions là "phép thuật" và do đó không an toàn. Trên thực tế, Server Actions có chức năng giống hệt như các endpoint API công khai. Khi bạn hiển thị một Server Action, nó có thể được gọi bởi bất kỳ ai có thể thực hiện yêu cầu HTTP đến miền của bạn.
Vì lý do này, bạn phải coi mọi Server Action là một điểm vào không đáng tin cậy:
FormData hoặc các đối số được truyền cho Server Action. Luôn sử dụng thư viện xác thực schema như Zod để làm sạch và xác thực đầu vào trước khi tương tác với cơ sở dữ liệu của bạn.export async function secureAction(data: unknown) {
"use server";
const session = await getSession();
if (!session) throw new Error("Unauthorized");
const parsed = mySchema.safeParse(data);
if (!parsed.success) throw new Error("Invalid input");
// Safe to proceed
}
Server Actions tỏa sáng nhất khi được sử dụng nguyên bản với các biểu mẫu HTML. Bằng cách truyền trực tiếp một Server Action vào thuộc tính action của một phần tử <form>, bạn sẽ có được cải tiến lũy tiến ngay lập tức.
import { createPost } from "./actions";
export default function PostForm() {
return (
<form action={createPost}>
<input type="text" name="title" required />
<button type="submit">Create</button>
</form>
);
}
Nếu JavaScript bị tắt hoặc không tải được, trình duyệt sẽ quay lại việc gửi biểu mẫu được mã hóa URL tiêu chuẩn. Next.js xử lý điều này một cách liền mạch, thực thi action và chuyển hướng người dùng trở lại trang đã hiển thị. Khi JavaScript được bật, React sẽ chặn việc gửi, ngăn tải lại toàn bộ trang và xử lý yêu cầu thông qua fetch, cho phép chuyển đổi mượt mà, giống như SPA.
React 19 giới thiệu các hook như useActionState và useFormStatus để xử lý trạng thái chờ và cập nhật giao diện người dùng lạc quan một cách nguyên bản. Điều này tiếp tục giảm sự phụ thuộc vào các thư viện quản lý trạng thái bên ngoài, đưa logic thay đổi dữ liệu trở lại nền tảng.
Next.js Server Actions đại diện cho một sự thay đổi cơ bản trong cách chúng ta kiến trúc các ứng dụng React full-stack. Bằng cách thu hẹp khoảng cách giữa client và server, giảm nhu cầu về các API route rõ ràng và tích hợp sâu sắc với các cơ chế bộ nhớ đệm, Server Actions cung cấp trải nghiệm nhà phát triển cực kỳ hiện đại nhưng vẫn dựa trên các tiêu chuẩn web cơ bản.
Mặc dù chúng yêu cầu một cách tiếp cận kỷ luật đối với bảo mật và xác thực, nhưng sự đánh đổi về năng suất và sự đơn giản là không thể phủ nhận. Khi hệ sinh thái React tiếp tục phát triển, mô hình thay đổi dữ liệu kiểu RPC được tiên phong bởi Server Actions được dự đoán sẽ trở thành tiêu chuẩn cho việc thay đổi dữ liệu trên toàn bộ framework.
Một nhà phát triển đang xây dựng ứng dụng Next.js sử dụng Server Actions để thay đổi dữ liệu. Họ có một form tạo bài đăng blog mới và sau đó cần hiển thị danh sách bài đăng đã cập nhật. Cách nào sau đây là hiệu quả và chuẩn nhất để đảm bảo UI phản ánh bài đăng mới sau khi Server Action hoàn tất thành công?
Làm sạch log AI CLI, tạo biểu thức cron, giải mã JWT và tính quyền chmod offline.

Nắm vững khi nào nên chọn Server Actions so với Route Handlers trong Next.js 15, phân tích toàn diện progressive enhancement, hành vi caching, giao thức RPC và các ranh giới bảo mật.
Đọc tiếp
Hướng dẫn toàn diện về các thành phần cache và server actions của Next.js 15: hướng dẫn kiến trúc sản xuất hoàn chỉnh với kiến trúc cấp độ sản xuất và các ví dụ code.
Đọc tiếp
Hướng dẫn kiến trúc chuyên sâu về bảo mật Next.js 15: tăng cường Server Actions chống lại CSRF, SSRF & State Injection với các ví dụ thực tế đã được kiểm chứng trong sản xuất.
Đọc tiếp