Skip to main content

Server Actions vs API Routes in Next.js: When to Use Each

Server Actions vs API Routes in Next.js: When to Use Each
nextjs#nextjs#server-actions#api-routes#react+3 more

Server Actions vs API Routes in Next.js: When to Use Each

Both work on the server, but smart usage of each makes your work clean, fast, and secure. Learn when to use Server Actions and when to stick with API Routes.

Mohammad Alhabil

Author

December 10, 2024
5 min read
~1000 words

Server Actions vs API Routes in Next.js: When to Use Each

Both actually work "on the server"... But smart usage of each? That's what makes your work look clean, fast, and secure.

A Real-World Analogy: Restaurant Kitchen ๐Ÿณ

ApproachAnalogy
API RouteLike an order-taking employee. You give them the order, they go to the kitchen, verify, and bring back the response after a while...
Server ActionYou're talking directly to the chef. You order, they execute immediately, everything happens in the moment without a middleman.

The Core Difference

API Routes

// app/api/posts/route.ts
export async function POST(request: Request) {
  const data = await request.json();
  
  // Validate, process, save to DB
  const post = await db.posts.create({ data });
  
  return Response.json(post);
}

Characteristics:

  • ๐Ÿ“ Write files in /api/*, each file is an endpoint
  • ๐Ÿ”„ Receives request from client, runs on server, returns JSON
  • โš ๏ธ Exposed and needs extra protection (CSRF, rate limiting)
  • ๐ŸŒ Network round-trip between client and server
  • ๐Ÿ”ง Focus: Flexibility, clear separation of concerns

Server Actions

// app/actions.ts
'use server'

export async function createPost(formData: FormData) {
  const title = formData.get('title');
  
  // Direct DB access, no endpoint needed
  const post = await db.posts.create({ 
    data: { title } 
  });
  
  revalidatePath('/posts');
  return post;
}

Characteristics:

  • ๐ŸŽฏ Functions inside Server Components
  • ๐Ÿš€ Call directly from UI without fetch or JSON
  • ๐Ÿ”’ No visible API endpoint = higher security
  • โšก Higher performance (no network round-trip)
  • ๐Ÿช Direct access to cookies, headers, session, DB

Side-by-Side Comparison

FeatureAPI RoutesServer Actions
Location/api/* folderAny server file
Call Methodfetch()Direct function call
ResponseJSONAny value
SecurityNeeds CSRF protectionBuilt-in security
PerformanceNetwork round-tripDirect execution
External Accessโœ… YesโŒ No
Best ForWebhooks, external APIsMutations, forms

When to Use Each?

โœ… Use Server Actions When:

  1. Working with App Router (Next.js 13+)
  2. Doing Mutations (create, update, delete)
  3. Form submissions directly from UI
  4. Performance matters (no round-trip)
  5. Security is priority (no exposed endpoint)

โœ… Use API Routes When:

  1. External webhooks (Stripe, GitHub, etc.)
  2. Mobile app backend needs endpoints
  3. Third-party integrations sending requests
  4. Legacy projects without Server Actions
  5. Complex debugging needs clear endpoints

Practical Examples

Example 1: Form Submission โ†’ Server Action โœ…

// app/actions.ts
'use server'

export async function registerUser(formData: FormData) {
  const email = formData.get('email') as string;
  const password = formData.get('password') as string;
  
  // Validate
  if (!email || !password) {
    return { error: 'Missing fields' };
  }
  
  // Create user
  const user = await db.users.create({
    data: { email, password: await hash(password) }
  });
  
  return { success: true, user };
}
// app/register/page.tsx
import { registerUser } from './actions';

export default function RegisterPage() {
  return (
    <form action={registerUser}>
      <input name="email" type="email" required />
      <input name="password" type="password" required />
      <button type="submit">Register</button>
    </form>
  );
}

Example 2: Stripe Webhook โ†’ API Route โœ…

// app/api/webhooks/stripe/route.ts
import { headers } from 'next/headers';
import Stripe from 'stripe';

export async function POST(request: Request) {
  const body = await request.text();
  const signature = headers().get('stripe-signature')!;
  
  const event = stripe.webhooks.constructEvent(
    body,
    signature,
    process.env.STRIPE_WEBHOOK_SECRET!
  );
  
  switch (event.type) {
    case 'payment_intent.succeeded':
      await handlePaymentSuccess(event.data.object);
      break;
  }
  
  return Response.json({ received: true });
}

Quick Decision Guide

Use CaseChoice
๐Ÿ“ User registration formServer Action
๐Ÿ—‘๏ธ Delete post from dashboardServer Action
๐Ÿ’ฌ Add commentServer Action
๐Ÿ’ณ Stripe webhookAPI Route
๐Ÿ“ฑ Mobile app endpointAPI Route
๐Ÿ”„ GitHub webhookAPI Route
โค๏ธ Like/unlike buttonServer Action
๐Ÿ“ค File uploadEither (depends)

Important Notes

๐Ÿ”’ Security

Server Actions are safer from CSRF attacks because there's no exposed endpoint. But always validate and authenticate inside your functions:

'use server'

export async function deletePost(postId: string) {
  const session = await getSession();
  
  if (!session) {
    throw new Error('Unauthorized');
  }
  
  // Check ownership
  const post = await db.posts.findUnique({ where: { id: postId } });
  
  if (post.authorId !== session.userId) {
    throw new Error('Forbidden');
  }
  
  await db.posts.delete({ where: { id: postId } });
}

๐Ÿ“Š Data Fetching

Server Actions are NOT primarily for data fetching โ€” they're for mutations. Data fetching belongs to:

  • Server Components (direct DB/API calls)
  • React Query / SWR (client-side)

โšก Performance

Server Actions are faster because they skip the full HTTP request-response cycle. The function runs directly on the server and returns the result.


Start understanding the UI... and the backend that powers it.

Interfaces you love, code you understand. ๐Ÿ’ก

Topics covered

#nextjs#server-actions#api-routes#react#backend#security#performance

Found this article helpful?

Share it with your network and help others learn too!

Mohammad Alhabil

Written by Mohammad Alhabil

Frontend Developer & Software Engineer passionate about building beautiful and functional web experiences. I write about React, Next.js, and modern web development.