Route Handlers

Back

Loading concept...

🚀 Next.js Route Handlers: Your Restaurant’s Kitchen Staff

Imagine you own a restaurant. Customers walk in and place orders (requests). But who makes the food? Who handles the cooking, plating, and serving?

That’s exactly what Route Handlers do in Next.js. They are your kitchen staff—they receive orders, process them, and send back exactly what the customer asked for.


🍳 Route Handlers Overview

What Are Route Handlers?

Think of Route Handlers as workers in your kitchen. Each worker has a specific station:

  • One handles GET orders (reading the menu)
  • One handles POST orders (placing new orders)
  • One handles DELETE orders (canceling orders)

In Real Life:

  • When your app needs to fetch user data → Route Handler
  • When your app needs to save a form → Route Handler
  • When your app needs to delete something → Route Handler

Why Do We Need Them?

Before Route Handlers, you needed a separate backend server. Now? Your Next.js app IS the server too!

graph TD A["User clicks button"] --> B["Frontend sends request"] B --> C["Route Handler receives it"] C --> D["Processes the request"] D --> E["Sends response back"] E --> F["User sees result"]

📁 The route.js File

Where Do Route Handlers Live?

Just like each kitchen station has its place, Route Handlers live in a special file called route.js (or route.ts for TypeScript).

The Rule: Put route.js inside the app folder, in the path you want.

app/
  api/
    users/
      route.js    ← handles /api/users
    products/
      route.js    ← handles /api/products

Simple Example

// app/api/hello/route.js

export async function GET() {
  return Response.json({
    message: "Hello there!"
  });
}

What happens?

  • Visit /api/hello in your browser
  • You see: {"message": "Hello there!"}

That’s it! Your first Route Handler is alive!


🛠️ Route Handler Methods

The Kitchen Staff Roles

Each HTTP method is like a different job in the kitchen:

Method Kitchen Job What It Does
GET Menu Reader Fetch data
POST Order Taker Create new data
PUT Chef Update all data
PATCH Helper Update some data
DELETE Busboy Remove data

Example: All Methods in One File

// app/api/tasks/route.js

// GET - Read all tasks
export async function GET() {
  return Response.json([
    { id: 1, task: "Learn Next.js" }
  ]);
}

// POST - Create a task
export async function POST(request) {
  const body = await request.json();
  return Response.json({
    created: body
  });
}

// DELETE - Remove a task
export async function DELETE() {
  return Response.json({
    deleted: true
  });
}

🎯 Remember: You only export the methods you need. Don’t need DELETE? Don’t include it!


📦 NextRequest Object

Your Order Slip

When a customer places an order, you get an order slip with all the details. NextRequest is that slip—it contains everything about the incoming request.

What’s Inside NextRequest?

// app/api/search/route.js
import { NextRequest } from 'next/server';

export async function GET(request) {
  // Get the full URL
  const url = request.nextUrl;

  // Get search parameters
  // Example: /api/search?q=pizza
  const query = url.searchParams.get('q');

  // Get the HTTP method
  const method = request.method;

  return Response.json({
    searched: query
  });
}

Real Example: Search Feature

// /api/search?term=shoes&color=red

export async function GET(request) {
  const { searchParams } = request.nextUrl;

  const term = searchParams.get('term');
  // → "shoes"

  const color = searchParams.get('color');
  // → "red"

  return Response.json({
    term,
    color
  });
}

📤 NextResponse Object

Your Food Delivery Tray

If NextRequest is the order slip, then NextResponse is the tray you use to send food back to the customer. It gives you superpowers over your response!

Basic Usage

import { NextResponse } from 'next/server';

export async function GET() {
  // Simple JSON response
  return NextResponse.json({
    name: "Pizza",
    price: 12.99
  });
}

Adding Status Codes

// Success!
return NextResponse.json(
  { message: "Created!" },
  { status: 201 }
);

// Oops, not found
return NextResponse.json(
  { error: "Not found" },
  { status: 404 }
);

Redirecting Users

import { NextResponse } from 'next/server';

export async function GET() {
  // Send user somewhere else
  return NextResponse.redirect(
    new URL('/login', request.url)
  );
}

🍪 The cookies Function

Your Customer’s Loyalty Card

Cookies are like loyalty cards. They remember things about the customer between visits. “Oh, you like extra cheese? I remember!”

Reading Cookies

import { cookies } from 'next/headers';

export async function GET() {
  const cookieStore = await cookies();

  // Read a cookie
  const theme = cookieStore.get('theme');

  return Response.json({
    theme: theme?.value
  });
}

Setting Cookies

import { cookies } from 'next/headers';

export async function POST() {
  const cookieStore = await cookies();

  // Set a cookie
  cookieStore.set('theme', 'dark');

  // Set with options
  cookieStore.set('token', 'abc123', {
    httpOnly: true,
    secure: true,
    maxAge: 60 * 60 * 24  // 1 day
  });

  return Response.json({
    success: true
  });
}

Deleting Cookies

export async function DELETE() {
  const cookieStore = await cookies();

  // Remove the cookie
  cookieStore.delete('theme');

  return Response.json({
    deleted: true
  });
}

📋 The headers Function

The Special Instructions Note

Headers are like special notes attached to an order. “Customer has allergies!” or “VIP customer, extra care!”

Reading Headers

import { headers } from 'next/headers';

export async function GET() {
  const headersList = await headers();

  // Get specific header
  const userAgent = headersList.get('user-agent');
  const auth = headersList.get('authorization');

  return Response.json({
    browser: userAgent
  });
}

Common Headers You’ll Use

Header What It Tells You
user-agent Browser/device info
authorization Login credentials
content-type Data format
accept-language User’s language

Setting Response Headers

import { NextResponse } from 'next/server';

export async function GET() {
  const response = NextResponse.json({
    data: "Hello"
  });

  // Add custom header
  response.headers.set(
    'X-Custom-Header',
    'my-value'
  );

  return response;
}

🎯 Dynamic Route Handlers

The Flexible Kitchen

Sometimes you need to handle orders for specific items. “I want pizza #42” or “Show me user #7”. Dynamic routes handle this!

The Magic: Square Brackets

Create a folder with square brackets: [id], [slug], [name]

app/
  api/
    users/
      [id]/
        route.js  ← handles /api/users/1, /api/users/2, etc.

Getting the Dynamic Value

// app/api/users/[id]/route.js

export async function GET(request, { params }) {
  const { id } = await params;

  // If URL is /api/users/42
  // id = "42"

  return Response.json({
    userId: id
  });
}

Multiple Dynamic Segments

app/
  api/
    shops/
      [shopId]/
        products/
          [productId]/
            route.js
// /api/shops/5/products/99

export async function GET(request, { params }) {
  const { shopId, productId } = await params;

  // shopId = "5"
  // productId = "99"

  return Response.json({
    shopId,
    productId
  });
}

Catch-All Routes

Want to catch everything after a certain point?

app/
  api/
    files/
      [...path]/
        route.js
// /api/files/images/2024/photo.jpg

export async function GET(request, { params }) {
  const { path } = await params;

  // path = ["images", "2024", "photo.jpg"]

  return Response.json({ path });
}

🎉 Putting It All Together

Here’s a complete example combining everything:

// app/api/products/[id]/route.js

import { NextResponse } from 'next/server';
import { cookies, headers } from 'next/headers';

export async function GET(request, { params }) {
  // 1. Get dynamic ID
  const { id } = await params;

  // 2. Check cookies
  const cookieStore = await cookies();
  const token = cookieStore.get('auth');

  // 3. Check headers
  const headersList = await headers();
  const lang = headersList.get('accept-language');

  // 4. Get query params
  const { searchParams } = request.nextUrl;
  const detailed = searchParams.get('detailed');

  // 5. Send response
  return NextResponse.json({
    productId: id,
    isLoggedIn: !!token,
    language: lang,
    showDetails: detailed === 'true'
  });
}

🚀 You Did It!

You now understand:

Route Handlers = Your kitchen staff handling requests

route.js = Where handlers live

Methods = GET, POST, PUT, PATCH, DELETE

NextRequest = The incoming order slip

NextResponse = Your response tray with superpowers

cookies() = Customer loyalty cards

headers() = Special instruction notes

Dynamic Routes = Flexible [id] folders

You’re ready to build amazing APIs! 🎊

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.