服务端阅读 02月17日 23:31
Next.js 的 API Routes 是如何工作的?
Next.js 提供了强大的 API Routes 功能,允许开发者创建 API 端点来处理服务器端逻辑。API Routes 可以处理数据库查询、身份验证、表单提交等服务器端操作。API Routes 基础创建 API Route在 pages/api 目录下创建文件,每个文件都会成为一个 API 端点。pages/ api/ hello.js → /api/hello users/ index.js → /api/users [id].js → /api/users/123基本示例// pages/api/hello.jsexport default function handler(req, res) { res.status(200).json({ message: 'Hello from Next.js API!' });}请求和响应对象请求对象 (req)req 对象包含以下属性:req.method:HTTP 方法(GET、POST、PUT、DELETE 等)req.query:查询参数req.body:请求体(POST、PUT 等)req.headers:请求头req.cookies:Cookie响应对象 (res)res 对象提供以下方法:res.status(code):设置状态码res.json(data):发送 JSON 响应res.send(data):发送响应res.redirect(url):重定向res.setHeader(name, value):设置响应头处理不同的 HTTP 方法// pages/api/users/[id].jsexport 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`); }}中间件自定义中间件// lib/middleware.jsexport function authMiddleware(req, res, next) { const token = req.headers.authorization; if (!token) { return res.status(401).json({ error: 'Unauthorized' }); } // 验证 token const user = verifyToken(token); if (!user) { return res.status(401).json({ error: 'Invalid token' }); } req.user = user; next();}使用中间件// pages/api/protected.jsimport { authMiddleware } from '@/lib/middleware';export default function handler(req, res) { // 受保护的路由 res.status(200).json({ user: req.user });}// 应用中间件export const config = { api: { bodyParser: false, externalResolver: true, },};// 在实际使用中,需要手动调用中间件数据库集成使用 Prisma// pages/api/posts/index.jsimport { 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(); }}使用 MongoDB// pages/api/users/index.jsimport 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(); }}身份验证使用 NextAuth.js// pages/api/auth/[...nextauth].jsimport 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) => { // 验证用户 const user = await authenticate(credentials); if (user) { return user; } return null; } }), ], database: process.env.DATABASE_URL,});JWT 验证// lib/auth.jsimport 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' });}文件上传// pages/api/upload.jsimport 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)}` }); });}错误处理// pages/api/error.jsexport default function handler(req, res) { try { // 业务逻辑 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 配置// pages/api/cors.jsexport 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' });}最佳实践使用环境变量:敏感信息存储在 .env 文件中验证输入:使用验证库如 Zod 或 Yup错误处理:统一错误处理格式日志记录:记录请求和错误信息速率限制:防止 API 滥用缓存:对频繁访问的数据进行缓存文档化:使用 Swagger 或 OpenAPI 文档化 APINext.js API Routes 提供了简单而强大的方式来构建服务器端 API,无需单独的后端服务器,使全栈开发变得更加便捷。