Next.js provides powerful API Routes functionality that allows developers to create API endpoints to handle server-side logic. API Routes can handle database queries, authentication, form submissions, and other server-side operations.
API Routes Basics
Creating an API Route
Create files in the pages/api directory, each file becomes an API endpoint.
shellpages/ api/ hello.js → /api/hello users/ index.js → /api/users [id].js → /api/users/123
Basic Example
javascript// pages/api/hello.js export default function handler(req, res) { res.status(200).json({ message: 'Hello from Next.js API!' }); }
Request and Response Objects
Request Object (req)
The req object contains the following properties:
req.method: HTTP method (GET, POST, PUT, DELETE, etc.)req.query: Query parametersreq.body: Request body (POST, PUT, etc.)req.headers: Request headersreq.cookies: Cookies
Response Object (res)
The res object provides the following methods:
res.status(code): Set status coderes.json(data): Send JSON responseres.send(data): Send responseres.redirect(url): Redirectres.setHeader(name, value): Set response header
Handling Different HTTP Methods
javascript// pages/api/users/[id].js export default async function handler(req, res) { const { id } = req.query; switch (req.method) { case 'GET': const user = await getUserById(id); res.status(200).json(user); break; case 'PUT': const updatedUser = await updateUser(id, req.body); res.status(200).json(updatedUser); break; case 'DELETE': await deleteUser(id); res.status(204).end(); break; default: res.setHeader('Allow', ['GET', 'PUT', 'DELETE']); res.status(405).end(`Method ${req.method} Not Allowed`); } }
Middleware
Custom Middleware
javascript// lib/middleware.js export function authMiddleware(req, res, next) { const token = req.headers.authorization; if (!token) { return res.status(401).json({ error: 'Unauthorized' }); } // Verify token const user = verifyToken(token); if (!user) { return res.status(401).json({ error: 'Invalid token' }); } req.user = user; next(); }
Using Middleware
javascript// pages/api/protected.js import { authMiddleware } from '@/lib/middleware'; export default function handler(req, res) { // Protected route res.status(200).json({ user: req.user }); } export const config = { api: { bodyParser: false, externalResolver: true, }, }; // In actual use, middleware needs to be called manually
Database Integration
Using Prisma
javascript// pages/api/posts/index.js import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); export default async function handler(req, res) { if (req.method === 'GET') { const posts = await prisma.post.findMany(); res.status(200).json(posts); } else if (req.method === 'POST') { const post = await prisma.post.create({ data: req.body, }); res.status(201).json(post); } else { res.status(405).end(); } }
Using MongoDB
javascript// pages/api/users/index.js import clientPromise from '@/lib/mongodb'; export default async function handler(req, res) { const client = await clientPromise; const db = client.db(); if (req.method === 'GET') { const users = await db.collection('users').find({}).toArray(); res.status(200).json(users); } else if (req.method === 'POST') { const result = await db.collection('users').insertOne(req.body); res.status(201).json(result); } else { res.status(405).end(); } }
Authentication
Using NextAuth.js
javascript// pages/api/auth/[...nextauth].js import NextAuth from 'next-auth'; import Providers from 'next-auth/providers'; export default NextAuth({ providers: [ Providers.Google({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, }), Providers.Credentials({ name: 'Credentials', credentials: { username: { label: "Username", type: "text" }, password: { label: "Password", type: "password" } }, authorize: async (credentials) => { // Verify user const user = await authenticate(credentials); if (user) { return user; } return null; } }), ], database: process.env.DATABASE_URL, });
JWT Verification
javascript// lib/auth.js import jwt from 'jsonwebtoken'; export function verifyToken(token) { try { return jwt.verify(token, process.env.JWT_SECRET); } catch (error) { return null; } } export function createToken(payload) { return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1d' }); }
File Upload
javascript// pages/api/upload.js import formidable from 'formidable'; import fs from 'fs'; import path from 'path'; export const config = { api: { bodyParser: false, }, }; export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).end(); } const form = formidable({ uploadDir: path.join(process.cwd(), '/public/uploads'), keepExtensions: true, }); form.parse(req, (err, fields, files) => { if (err) { return res.status(500).json({ error: 'File upload failed' }); } const file = files.file[0]; res.status(200).json({ url: `/uploads/${path.basename(file.filepath)}` }); }); }
Error Handling
javascript// pages/api/error.js export default function handler(req, res) { try { // Business logic const data = processData(req.body); res.status(200).json(data); } catch (error) { console.error('API Error:', error); if (error.name === 'ValidationError') { res.status(400).json({ error: error.message }); } else if (error.name === 'UnauthorizedError') { res.status(401).json({ error: 'Unauthorized' }); } else { res.status(500).json({ error: 'Internal server error' }); } } }
CORS Configuration
javascript// pages/api/cors.js export default function handler(req, res) { res.setHeader('Access-Control-Allow-Credentials', true); res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS,PATCH,DELETE,POST,PUT'); res.setHeader( 'Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version' ); if (req.method === 'OPTIONS') { res.status(200).end(); return; } res.status(200).json({ message: 'CORS enabled' }); }
Best Practices
- Use environment variables: Store sensitive information in
.envfiles - Validate input: Use validation libraries like Zod or Yup
- Error handling: Unified error handling format
- Logging: Record requests and error information
- Rate limiting: Prevent API abuse
- Caching: Cache frequently accessed data
- Documentation: Document APIs using Swagger or OpenAPI
Next.js API Routes provides a simple yet powerful way to build server-side APIs without needing a separate backend server, making full-stack development more convenient.