Appearance
第10章:API 路由开发(Next.js 内置后端能力)
Next.js 不仅是一个前端框架,还提供了内置的后端能力,通过 API 路由可以直接在 Next.js 项目中创建 API 接口,无需单独搭建后端服务。这使得 Next.js 成为一个全栈框架,非常适合快速开发完整的应用。
10.1 API 路由基础
10.1.1 API 路由目录约定
在 Next.js 14 中,API 路由位于 app/api 目录下,遵循与 App Router 相同的文件系统路由约定。每个 API 路由文件需要导出一个函数,处理特定的 HTTP 请求。
基本结构示例:
javascript
// app/api/hello/route.js
export async function GET(request) {
return new Response('Hello, Next.js!');
}10.1.2 API 路由文件命名
- API 路由文件必须命名为
route.js或route.ts - 目录结构对应 API 路径,例如
app/api/users/route.js对应/api/users路径 - 支持动态路由,例如
app/api/users/[id]/route.js对应/api/users/123路径
10.2 常用请求方法实现
Next.js API 路由支持多种 HTTP 请求方法,通过导出不同的函数来处理:
10.2.1 GET 请求
用于获取数据:
javascript
// app/api/users/route.js
export async function GET(request) {
// 从数据库获取用户列表
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
return Response.json(users);
}10.2.2 POST 请求
用于创建数据:
javascript
// app/api/users/route.js
export async function POST(request) {
const data = await request.json();
// 处理创建用户的逻辑
const newUser = {
id: Date.now(),
...data
};
// 模拟保存到数据库
// db.users.insert(newUser);
return Response.json(newUser, { status: 201 });
}10.2.3 PUT 请求
用于更新数据:
javascript
// app/api/users/[id]/route.js
export async function PUT(request, { params }) {
const { id } = params;
const data = await request.json();
// 处理更新用户的逻辑
const updatedUser = {
id: parseInt(id),
...data
};
// 模拟更新数据库
// db.users.update({ id }, data);
return Response.json(updatedUser);
}10.2.4 DELETE 请求
用于删除数据:
javascript
// app/api/users/[id]/route.js
export async function DELETE(request, { params }) {
const { id } = params;
// 处理删除用户的逻辑
// db.users.delete({ id });
return Response.json({ message: `User ${id} deleted` });
}10.3 API 路由参数与请求体处理
10.3.1 路径参数
通过 params 对象获取路径参数:
javascript
// app/api/users/[id]/route.js
export async function GET(request, { params }) {
const { id } = params;
// 根据 id 获取用户
return Response.json({ id, name: 'User ' + id });
}10.3.2 查询参数
通过 request.nextUrl.searchParams 获取查询参数:
javascript
// app/api/users/route.js
export async function GET(request) {
const searchParams = request.nextUrl.searchParams;
const page = searchParams.get('page') || 1;
const limit = searchParams.get('limit') || 10;
// 根据分页参数获取用户列表
return Response.json({
users: [],
pagination: { page, limit }
});
}10.3.3 请求体处理
对于 POST、PUT 等请求,需要解析请求体:
javascript
// app/api/users/route.js
export async function POST(request) {
try {
const data = await request.json();
// 处理数据
return Response.json({ success: true, data });
} catch (error) {
return Response.json({ error: 'Invalid JSON' }, { status: 400 });
}
}10.4 API 路由中间件
10.4.1 权限验证中间件
创建一个权限验证中间件:
javascript
// app/api/middleware.js
import { NextResponse } from 'next/server';
export function middleware(request) {
const token = request.headers.get('authorization')?.split(' ')[1];
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
// 验证 token
// const isValid = verifyToken(token);
// if (!isValid) {
// return NextResponse.json({ error: 'Invalid token' }, { status: 401 });
// }
return NextResponse.next();
}
export const config = {
matcher: '/api/protected/:path*',
};10.4.2 请求日志中间件
javascript
// app/api/logging-middleware.js
import { NextResponse } from 'next/server';
export function middleware(request) {
console.log('API Request:', {
url: request.url,
method: request.method,
headers: Object.fromEntries(request.headers),
});
return NextResponse.next();
}
export const config = {
matcher: '/api/:path*',
};10.5 与数据库连接
10.5.1 MongoDB 集成
步骤 1:安装依赖
bash
npm install mongodb步骤 2:创建数据库连接
javascript
// lib/mongodb.js
import { MongoClient } from 'mongodb';
const uri = process.env.MONGODB_URI;
const options = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
let client;
let clientPromise;
if (!uri) {
throw new Error('Please add your Mongo URI to .env.local');
}
if (process.env.NODE_ENV === 'development') {
if (!global._mongoClientPromise) {
client = new MongoClient(uri, options);
global._mongoClientPromise = client.connect();
}
clientPromise = global._mongoClientPromise;
} else {
client = new MongoClient(uri, options);
clientPromise = client.connect();
}
export default clientPromise;步骤 3:在 API 路由中使用
javascript
// app/api/users/route.js
import clientPromise from '@/lib/mongodb';
export async function GET(request) {
try {
const client = await clientPromise;
const db = client.db('nextjs-tutorial');
const users = await db.collection('users').find({}).toArray();
return Response.json(users);
} catch (error) {
return Response.json({ error: error.message }, { status: 500 });
}
}10.5.2 PostgreSQL 集成
步骤 1:安装依赖
bash
npm install pg步骤 2:创建数据库连接
javascript
// lib/postgres.js
import { Pool } from 'pg';
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
export default pool;步骤 3:在 API 路由中使用
javascript
// app/api/users/route.js
import pool from '@/lib/postgres';
export async function GET(request) {
try {
const { rows } = await pool.query('SELECT * FROM users');
return Response.json(rows);
} catch (error) {
return Response.json({ error: error.message }, { status: 500 });
}
}10.6 API 路由最佳实践
- 错误处理:始终使用 try/catch 捕获错误并返回适当的错误响应
- 输入验证:验证请求数据的格式和内容
- 安全性:使用参数化查询防止 SQL 注入,验证用户权限
- 性能优化:合理使用缓存,避免重复数据库查询
- 代码组织:将数据库逻辑和业务逻辑分离,保持 API 路由文件简洁
10.7 实战示例:完整的用户 API
javascript
// app/api/users/route.js
import clientPromise from '@/lib/mongodb';
export async function GET(request) {
try {
const client = await clientPromise;
const db = client.db('nextjs-tutorial');
const users = await db.collection('users').find({}).toArray();
return Response.json(users);
} catch (error) {
return Response.json({ error: error.message }, { status: 500 });
}
}
export async function POST(request) {
try {
const client = await clientPromise;
const db = client.db('nextjs-tutorial');
const data = await request.json();
const result = await db.collection('users').insertOne(data);
return Response.json({
...data,
_id: result.insertedId
}, { status: 201 });
} catch (error) {
return Response.json({ error: error.message }, { status: 500 });
}
}
// app/api/users/[id]/route.js
import clientPromise from '@/lib/mongodb';
import { ObjectId } from 'mongodb';
export async function GET(request, { params }) {
try {
const client = await clientPromise;
const db = client.db('nextjs-tutorial');
const user = await db.collection('users').findOne({
_id: new ObjectId(params.id)
});
if (!user) {
return Response.json({ error: 'User not found' }, { status: 404 });
}
return Response.json(user);
} catch (error) {
return Response.json({ error: error.message }, { status: 500 });
}
}
export async function PUT(request, { params }) {
try {
const client = await clientPromise;
const db = client.db('nextjs-tutorial');
const data = await request.json();
const result = await db.collection('users').updateOne(
{ _id: new ObjectId(params.id) },
{ $set: data }
);
if (result.matchedCount === 0) {
return Response.json({ error: 'User not found' }, { status: 404 });
}
return Response.json({ ...data, _id: params.id });
} catch (error) {
return Response.json({ error: error.message }, { status: 500 });
}
}
export async function DELETE(request, { params }) {
try {
const client = await clientPromise;
const db = client.db('nextjs-tutorial');
const result = await db.collection('users').deleteOne({
_id: new ObjectId(params.id)
});
if (result.deletedCount === 0) {
return Response.json({ error: 'User not found' }, { status: 404 });
}
return Response.json({ message: 'User deleted successfully' });
} catch (error) {
return Response.json({ error: error.message }, { status: 500 });
}
}通过本章的学习,你已经掌握了 Next.js API 路由的核心概念和使用方法。API 路由是 Next.js 的强大特性之一,它使得前后端开发可以在同一个项目中完成,大大简化了开发流程。在实际项目中,你可以根据具体需求创建各种 API 接口,与数据库进行交互,实现完整的业务逻辑。
