Using Next.js Route Handlers: A Complete Guide

What Are Next.js Route Handlers?

Setting Up Your First Route Handler

1import { NextResponse } from 'next/server';2 3export async function GET() {4  return NextResponse.json({ message: 'Hello, Route Handlers!' });5}6 7export async function POST(request: Request) {8  const data = await request.json();9  return NextResponse.json({ received: data }, { status: 201 });10}

Working with Dynamic Routes

1// app/api/posts/[id]/route.ts2import { NextResponse } from 'next/server';3 4export async function GET(5  request: Request,6  { params }: { params: { id: string } }7) {8  const postId = params.id;9  return NextResponse.json({ id: postId, title: 'Sample Post' });10}11 12export async function PUT(13  request: Request,14  { params }: { params: { id: string } }15) {16  const updates = await request.json();17  return NextResponse.json({ id: params.id, ...updates });18}19 20export async function DELETE(21  request: Request,22  { params }: { params: { id: string } }23) {24  return NextResponse.json({ success: true, id: params.id });25}

Handling Request Data

1import { NextRequest, NextResponse } from 'next/server';2 3export async function POST(request: NextRequest) {4  // Access query parameters5  const searchParams = request.nextUrl.searchParams;6  const action = searchParams.get('action');7 8  // Access headers9  const contentType = request.headers.get('content-type');10  const authorization = request.headers.get('authorization');11 12  // Read request body13  const body = await request.json();14  const { email, name } = body;15 16  // Access cookies17  const sessionToken = request.cookies.get('session_token');18 19  // Set response cookies20  const response = NextResponse.json({ received: { email, name } });21  response.cookies.set('request_id', Date.now().toString(), {22    httpOnly: true,23    secure: process.env.NODE_ENV === 'production',24    sameSite: 'strict'25  });26 27  return response;28}

Building RESTful API Patterns

1// app/api/posts/route.ts2import { NextRequest, NextResponse } from 'next/server';3 4// GET /api/posts - List all posts with pagination5export async function GET(request: NextRequest) {6  const { searchParams } = request.nextUrl;7  const page = parseInt(searchParams.get('page') || '1');8  const limit = parseInt(searchParams.get('limit') || '10');9 10  const posts = await fetchPosts({ page, limit });11  const total = await countPosts();12 13  return NextResponse.json({14    data: posts,15    pagination: {16      page,17      limit,18      total,19      totalPages: Math.ceil(total / limit)20    }21  });22}23 24// POST /api/posts - Create a new post25export async function POST(request: NextRequest) {26  const body = await request.json();27 28  if (!body.title || !body.content) {29    return NextResponse.json(30      { error: 'Title and content are required' },31      { status: 400 }32    );33  }34 35  const post = await createPost(body);36  return NextResponse.json(post, { status: 201 });37}

Security Best Practices

1import { NextRequest, NextResponse } from 'next/server';2import { verifyToken } from '@/lib/auth';3import { validatePost } from '@/lib/validation';4 5export async function POST(request: NextRequest) {6  // Authentication check7  const authHeader = request.headers.get('authorization');8  const token = authHeader?.replace('Bearer ', '');9 10  if (!token) {11    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });12  }13 14  const user = await verifyToken(token);15  if (!user) {16    return NextResponse.json({ error: 'Invalid token' }, { status: 401 });17  }18 19  // Input validation20  const body = await request.json();21  const validation = validatePost(body);22 23  if (!validation.isValid) {24    return NextResponse.json(25      { error: 'Validation failed', details: validation.errors },26      { status: 400 }27    );28  }29 30  // Authorization check31  if (!user.canCreatePosts) {32    return NextResponse.json({ error: 'Forbidden' }, { status: 403 });33  }34 35  const post = await createPost({ ...body, authorId: user.id });36  return NextResponse.json(post, { status: 201 });37}

Performance and Optimization

Advanced Patterns and Use Cases

1// CSV file generation endpoint2import { NextRequest, NextResponse } from 'next/server';3 4export async function GET(request: NextRequest) {5  const { searchParams } = request.nextUrl;6  const format = searchParams.get('format') || 'csv';7 8  const data = await exportData();9 10  if (format === 'csv') {11    const csv = convertToCSV(data);12    return new NextResponse(csv, {13      headers: {14        'Content-Type': 'text/csv',15        'Content-Disposition': `attachment; filename="export-${Date.now()}.csv"`16      }17    });18  }19 20  return NextResponse.json({ data, format: 'json' });21}22 23// Webhook handler with signature verification24import { createHmac } from 'crypto';25 26export async function POST(request: NextRequest) {27  const body = await request.text();28  const signature = request.headers.get('x-webhook-signature');29  const secret = process.env.WEBHOOK_SECRET;30 31  const expectedSignature = createHmac('sha256', secret)32    .update(body)33    .digest('hex');34 35  if (signature !== expectedSignature) {36    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });37  }38 39  const event = JSON.parse(body);40  await processWebhookEvent(event);41 42  return NextResponse.json({ received: true });43}

Error Handling and Debugging

Testing Route Handlers

Conclusion

Sources

Related Resources

Ready to Build Powerful APIs with Next.js?