🚀 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/helloin 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! 🎊
