API Routes & Server Actions
Part of the Full Stack Developer Roadmap
In short
Next.js lets you write backend code right next to your pages. Route handlers give you real HTTP endpoints, and server actions let a form call a server function directly. Each fits a different job.
Next.js's built-in ways to write backend logic inside the same project as your frontend — API routes for traditional endpoints, Server Actions for form-submission-style mutations callable directly from components.

Think of it like this
Postbox vs the society intercom
A postbox on the street takes letters from anyone, from anywhere. The intercom in your building connects only your flat to the society office, with one button press. Route handlers are the postbox: any client can call them. Server actions are the intercom, made for your own app's forms.
Watch it happen
Two ways to run server code
Route handler
- app/api/notes/route.ts exports GET and POST
- Any client: mobile app, webhook, curl
- You handle JSON, status codes, caching
Server action
- An async function marked 'use server'
- Called from your forms or client code
- Refreshes the page data after a write
Use server actions for your own app's writes. Use route handlers when something outside your React UI must call you.
The big ideas
What you need to know
Route handlers are endpoints
A route.ts file exports functions named after HTTP methods. Each receives a Request and returns a Response.
Server actions are functions
Mark an async function with 'use server'. Next.js turns calls to it into a POST request behind the scenes.
Both are public
A server action still has an endpoint under the hood. Check the session and validate input inside it, every time.
Refresh after writes
Call revalidatePath or revalidateTag in an action so server components show the new data.
See it in code
Walk through the code
1// app/api/notes/route.ts: a real endpoint for any client2export async function GET() {3 const notes = await db.note.findMany();4 return Response.json(notes);5}6// app/notes/actions.ts: a server action for your own forms7"use server";8import { revalidatePath } from "next/cache";9export async function createNote(formData: FormData) {10 const session = await auth();11 if (!session) throw new Error("Not signed in");12 const text = String(formData.get("text") ?? "").trim();13 if (!text) return;14 await db.note.create({ data: { text, userId: session.user.id } });15 revalidatePath("/notes");16}1/4GET /api/notes returns JSON. A mobile app or curl can call it like any API.
Use the action as <form action={createNote}>. auth() stands for your auth library's session helper, such as the one Auth.js gives you.
Save yourself the pain
Common mistakes
Oops
Creating an API route just so your own form can fetch it from a client component.
Do this instead
For your own UI, a server action is less code: no fetch, no JSON parsing, and the page refreshes itself.
Oops
Skipping auth checks in a server action because 'only my form calls it'.
Do this instead
Anyone can call it with a crafted request. Check the session and validate input in every action.
Your mission
One endpoint, one action
Quick check
Your React Native app also needs to read the notes. What should you build?
Go deeper
- 1Build a simple API route and call it from a client component.
- 2Build a form that submits directly to a Server Action instead of a separate API call.
- 3Compare when you'd reach for one vs the other.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.