API Routes & Server Actions
Next.js provides two patterns for server-side logic: Route Handlers for traditional API endpoints and Server Actions for form handling and data mutations.
Route Handlers
Define HTTP endpoints in the app/api/ directory using the Web Request and Response APIs.
// app/api/users/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
const users = await db.query("SELECT * FROM users");
return NextResponse.json(users);
}
export async function POST(request: NextRequest) {
const body = await request.json();
const user = await db.insert("users", body);
return NextResponse.json(user, { status: 201 });
}
Supported HTTP Methods
| Method | Use Case |
|---|---|
GET | Fetch data |
POST | Create a resource |
PUT | Replace a resource |
PATCH | Partially update a resource |
DELETE | Remove a resource |
Dynamic Route Handlers
// app/api/users/[id]/route.ts
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
const user = await db.findById("users", id);
if (!user) {
return NextResponse.json({ error: "Not found" }, { status: 404 });
}
return NextResponse.json(user);
}
Server Actions
Server Actions run on the server and can be called directly from Client Components.
Mark a function with "use server" to create an action.
// app/actions/create-post.ts
"use server";
import { revalidatePath } from "next/cache";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
const content = formData.get("content") as string;
await db.insert("posts", { title, content });
revalidatePath("/posts");
}
Using Actions in Forms
// app/posts/new/page.tsx
import { createPost } from "@/app/actions/create-post";
export default function NewPostPage() {
return (
<form action={createPost}>
<input name="title" placeholder="Post title" required />
<textarea name="content" placeholder="Write your post..." required />
<button type="submit">Publish</button>
</form>
);
}
Calling Actions from Client Components
"use client";
import { useTransition } from "react";
import { createPost } from "@/app/actions/create-post";
export function PublishButton() {
const [isPending, startTransition] = useTransition();
const handleClick = () => {
const formData = new FormData();
formData.set("title", "My Post");
formData.set("content", "Hello world");
startTransition(() => createPost(formData));
};
return (
<button onClick={handleClick} disabled={isPending}>
{isPending ? "Publishing..." : "Publish"}
</button>
);
}
When to Use Each Pattern
| Scenario | Use |
|---|---|
| Third-party API proxy | Route Handler |
| Webhook receiver | Route Handler |
| Form submission | Server Action |
| Data mutation from UI | Server Action |
| Public REST API | Route Handler |
| Revalidating cached data | Server Action |
Server Actions are the preferred pattern for mutations in Next.js. Use Route Handlers when you need a traditional HTTP endpoint (webhooks, external consumers, etc.).