Server Actions Deep Dive: Replace Your API Routes
A comprehensive guide to Next.js Server Actions — when they replace API routes, how they work under the hood, and advanced patterns.
Server Actions have quietly become the default mutation pattern in Next.js. Yet many developers still reach for API routes out of habit. If you're writing fetch('/api/something', { method: 'POST' }) for internal mutations, you're doing unnecessary work. Let me show you why — and how to use Server Actions for everything from simple forms to complex workflows.
What Server Actions Actually Do
A Server Action is an async function that runs on the server, invoked directly from your client components. Next.js handles the network layer — no fetch calls, no API route files, no request/response parsing. You write a function, call it, and get the result.
// actions/blog-post.actions.ts
"use server";
import { prisma } from '@/lib/prisma';
import { revalidatePath } from 'next/cache';
import { auth } from '@/lib/auth';
import { z } from 'zod';
const createPostSchema = z.object({
title: z.string().min(3).max(200),
content: z.string().min(50),
categoryId: z.string().uuid().optional(),
});
export async function createBlogPost(formData: FormData) {
const session = await auth();
if (!session?.user) throw new Error('Unauthorized');
const parsed = createPostSchema.safeParse({
title: formData.get('title'),
content: formData.get('content'),
categoryId: formData.get('categoryId'),
});
if (!parsed.success) {
return { error: parsed.error.flatten().fieldErrors };
}
const post = await prisma.blogPost.create({
data: {
...parsed.data,
slug: slugify(parsed.data.title),
authorId: session.user.id,
status: 'DRAFT',
articleType: 'COMMUNITY',
},
});
revalidatePath('/blog');
return { success: true, postId: post.id };
}
When Server Actions Replace API Routes
Use Server Actions for any mutation triggered by your own frontend:
- Form submissions (create, update, delete)
- Toggle actions (like, bookmark, follow)
- File uploads (with FormData)
- State changes (publish, archive, approve)
Keep API routes for:
- Webhooks from external services
- Public APIs consumed by third parties
- OAuth callbacks
- Endpoints that non-browser clients need to call
Advanced Pattern: Optimistic Updates
Combine Server Actions with React 19's useOptimistic for instant UI feedback:
"use client";
import { useOptimistic, useTransition } from "react";
import { toggleLike } from "@/actions/blog-post.actions";
export function LikeButton({ postId, initialLiked, initialCount }: Props) {
const [isPending, startTransition] = useTransition();
const [optimistic, setOptimistic] = useOptimistic(
{ liked: initialLiked, count: initialCount },
(current, newLiked: boolean) => ({
liked: newLiked,
count: current.count + (newLiked ? 1 : -1),
})
);
const handleClick = () => {
startTransition(async () => {
setOptimistic(!optimistic.liked);
await toggleLike(postId);
});
};
return (
<button onClick={handleClick} disabled={isPending}>
{optimistic.liked ? "Unlike" : "Like"} ({optimistic.count})
</button>
);
}
The UI updates instantly. If the server action fails, React automatically reverts the optimistic state. No manual rollback logic needed.
Security Considerations
Server Actions are public HTTP endpoints under the hood — anyone can call them. Always:
- Authenticate — check the session in every action
- Authorize — verify the user has permission for the specific operation
- Validate — parse all input with Zod or similar
- Rate limit — protect against abuse (use Redis counters)
Never trust that a Server Action is only called from your UI. Treat it like any other API endpoint from a security perspective.
The Bottom Line
Server Actions are not a toy feature for simple forms. They're a full mutation layer that replaces 90% of internal API routes while giving you better type safety, progressive enhancement, and integration with React's concurrent features. If you're still writing /api/ routes for your own frontend to call, make the switch.
Admin
Cal.com
Open source scheduling — tự host booking system, thay thế Calendly. Free & privacy-first.
Bình luận (0)
Đăng nhập để bình luận
Chưa có bình luận nào. Hãy là người đầu tiên!