Courses Curriculum Cities Blog Enroll Now
Next.js Full-Stack · Day 2 of 5 ~40 minutes

Day 2: API Routes and Server Actions

Build full-stack functionality: REST API routes, form handling with Server Actions, and data fetching patterns that avoid unnecessary client-side JavaScript.

1
Day 1
2
Day 2
3
Day 3
4
Day 4
5
Day 5
What You'll Build

A Next.js app with a working contact form that uses a Server Action to validate and save data — no API route needed, no useState, just a form that works.

1
Section 1 · 10 min

API Routes with Route Handlers

API routes in the App Router are called Route Handlers. Create a route.ts file in any directory under app/ to define HTTP methods as exported functions.

typescriptsrc/app/api/posts/route.ts
import { NextResponse } from 'next/server'

const posts = [
  { id: 1, title: 'Hello World' },
  { id: 2, title: 'Next.js is Great' },
]

// GET /api/posts
export async function GET() {
  return NextResponse.json(posts)
}

// POST /api/posts
export async function POST(request: Request) {
  const body = await request.json()

  if (!body.title) {
    return NextResponse.json(
      { error: 'Title required' },
      { status: 400 }
    )
  }

  const newPost = { id: posts.length + 1, ...body }
  posts.push(newPost)
  return NextResponse.json(newPost, { status: 201 })
}
2
Section 2 · 12 min

Server Actions — Forms Without APIs

Server Actions let you define server-side functions that forms call directly — no API route, no fetch() call, no useState. The form submits and the action runs on the server. Works even with JavaScript disabled.

typescriptsrc/app/contact/page.tsx
'use server'  // at the top of an actions file

import { revalidatePath } from 'next/cache'

async function submitContact(formData: FormData) {
  'use server'  // or inline like this

  const name = formData.get('name') as string
  const email = formData.get('email') as string
  const message = formData.get('message') as string

  // Validate
  if (!name || !email || !message) {
    throw new Error('All fields required')
  }

  // Save to DB, send email, etc.
  console.log({ name, email, message })

  // Revalidate the page cache
  revalidatePath('/contact')
}

export default function ContactPage() {
  return (
    <form action={submitContact}>
      <input name="name" required />
      <input name="email" type="email" required />
      <textarea name="message" required />
      <button type="submit">Send</button>
    </form>
  )
}
3
Section 3 · 9 min

Data Fetching Patterns

Next.js has three data fetching patterns. Understanding when to use each one is the key to building fast apps.

Data Fetching Patterns
Static (SSG)
Data fetched at build time. Ultra-fast. For content that rarely changes (blog, docs).
Server-side (SSR)
Data fetched on every request. Always fresh. For user-specific or real-time data.
Incremental (ISR)
Static, but revalidates after N seconds. Best of both worlds for most sites.
typescriptsrc/app/blog/page.tsx
// ISR: revalidate every 60 seconds
export const revalidate = 60

// Or disable caching entirely (SSR)
export const dynamic = 'force-dynamic'

// Or make fully static (SSG)
export const dynamic = 'force-static'

async function getBlogPosts() {
  const res = await fetch('/api/posts', {
    next: { revalidate: 60 }  // ISR on per-fetch level
  })
  return res.json()
}
4
Section 4 · 9 min

Middleware and Protected Routes

Middleware runs before every request and can redirect, rewrite, or add headers. Use it to protect routes without loading the full page first.

typescriptsrc/middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const token = request.cookies.get('auth-token')

  // Protect /dashboard routes
  if (request.nextUrl.pathname.startsWith('/dashboard') && !token) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*', '/api/protected/:path*']
}

What You Learned Today

  • Built REST API route handlers for GET and POST with proper status codes
  • Used Server Actions to handle form submission without any client-side fetch or state
  • Understood the three data fetching modes: SSG, SSR, and ISR — and when to use each
  • Added route protection via Next.js Middleware that redirects unauthenticated users
Your Challenge

Go Further on Your Own

  • Add optimistic updates to a Server Action using useOptimistic from React
  • Create an intercepting route that shows a modal instead of a new page (Next.js parallel routes)
  • Add rate limiting to your API route using the Upstash Redis rate limiter
Day 2 Complete

Nice work. Keep going.

Day 3 is ready when you are.

Continue to Day 3
Course Progress
40%

Want live instruction and hands-on projects? Join the AI bootcamp — 3 days, 5 cities.

Finished this lesson?