跳到主要内容

API 路由

Next.js 允许在同一项目中创建 API 接口,无需单独的后端服务。本章将介绍如何使用 Route Handlers 构建 API。

Route Handlers​

在 App Router 中,使用 route.ts 文件定义 API 路由。

基本 API​

// src/app/api/hello/route.ts
import { NextResponse } from "next/server";

export async function GET() {
return NextResponse.json({
message: "Hello from API",
});
}

访问 /api/hello 将返回 JSON 响应。

HTTP 方法​

支持所有 HTTP 方法:

// src/app/api/posts/route.ts
import { NextResponse } from "next/server";

// GET /api/posts
export async function GET() {
const posts = await getPosts();
return NextResponse.json(posts);
}

// POST /api/posts
export async function POST(request: Request) {
const body = await request.json();
const post = await createPost(body);
return NextResponse.json(post, { status: 201 });
}

// PUT /api/posts
export async function PUT(request: Request) {
const body = await request.json();
const post = await updatePost(body);
return NextResponse.json(post);
}

// DELETE /api/posts
export async function DELETE(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get("id");
await deletePost(id);
return new NextResponse(null, { status: 204 });
}

请求处理​

获取请求体​

export async function POST(request: Request) {
// JSON 数据
const json = await request.json();

// 表单数据
const formData = await request.formData();
const name = formData.get("name");

// 文本数据
const text = await request.text();

return NextResponse.json({ received: json });
}

获取 URL 参数​

在 Next.js 15 中,Route Handlers 的 params 也是 Promise 类型:

// app/api/posts/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
const post = await getPost(id);
return NextResponse.json(post);
}

获取查询参数​

export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const page = searchParams.get("page") || "1";
const limit = searchParams.get("limit") || "10";

const posts = await getPosts({ page, limit });
return NextResponse.json(posts);
}

获取请求头​

export async function GET(request: Request) {
const authorization = request.headers.get("authorization");
const userAgent = request.headers.get("user-agent");

return NextResponse.json({ authorization, userAgent });
}

在 Next.js 15 中,cookies() 函数返回 Promise,需要使用 await:

import { cookies } from "next/headers";

export async function GET() {
const cookieStore = await cookies();
const token = cookieStore.get("token");

return NextResponse.json({ token: token?.value });
}
Next.js 15 变更

在 Next.js 14 及更早版本中,cookies() 是同步函数。从 Next.js 15 开始,它返回 Promise。如果需要临时使用同步方式,可以使用类型断言:

import { cookies, type UnsafeUnwrappedCookies } from "next/headers";
// 会在开发环境显示警告
const cookieStore = cookies() as unknown as UnsafeUnwrappedCookies;

但推荐尽快迁移到异步用法。

响应处理​

JSON 响应​

import { NextResponse } from "next/server";

export async function GET() {
return NextResponse.json(
{ message: "Success", data: [] },
{ status: 200 }
);
}

自定义响应头​

export async function GET() {
return NextResponse.json(
{ message: "Success" },
{
headers: {
"Cache-Control": "public, max-age=3600",
"X-Custom-Header": "value",
},
}
);
}

重定向​

import { redirect } from "next/navigation";

export async function GET() {
redirect("/login");
}
import { NextResponse } from "next/server";

export async function POST(request: Request) {
const body = await request.json();

const response = NextResponse.json({ success: true });

response.cookies.set("token", "your-jwt-token", {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 60 * 60 * 24 * 7, // 7 天
});

return response;
}

动态路由​

动态段​

在 Next.js 15 中,params 是 Promise 类型,需要使用 async/await 获取值:

// app/api/users/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
const user = await getUser(id);

if (!user) {
return NextResponse.json({ error: "用户不存在" }, { status: 404 });
}

return NextResponse.json(user);
}

捕获所有路由​

// app/api/docs/[...slug]/route.ts
export async function GET(
request: Request,
{ params }: { params: Promise<{ slug: string[] }> }
) {
const { slug } = await params;
const path = slug.join("/");
const doc = await getDoc(path);

return NextResponse.json(doc);
}

中间件集成​

在 API 路由中使用中间件验证:

// src/app/api/admin/route.ts
import { NextResponse } from "next/server";
import { cookies } from "next/headers";

async function verifyAuth() {
const cookieStore = await cookies();
const token = cookieStore.get("token");
if (!token) return null;
return verifyToken(token.value);
}

export async function GET() {
const user = await verifyAuth();

if (!user) {
return NextResponse.json({ error: "未授权" }, { status: 401 });
}

return NextResponse.json({ user });
}

实战示例​

用户注册 API​

// src/app/api/auth/register/route.ts
import { NextResponse } from "next/server";
import { hash } from "bcrypt";
import { z } from "zod";

const registerSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
name: z.string().min(2),
});

export async function POST(request: Request) {
try {
const body = await request.json();

// 验证输入
const { email, password, name } = registerSchema.parse(body);

// 检查用户是否存在
const existingUser = await db.user.findUnique({ where: { email } });
if (existingUser) {
return NextResponse.json(
{ error: "邮箱已被注册" },
{ status: 400 }
);
}

// 创建用户
const hashedPassword = await hash(password, 10);
const user = await db.user.create({
data: { email, password: hashedPassword, name },
});

return NextResponse.json(
{ id: user.id, email: user.email, name: user.name },
{ status: 201 }
);
} catch (error) {
return NextResponse.json(
{ error: "注册失败" },
{ status: 500 }
);
}
}

文件上传 API​

// src/app/api/upload/route.ts
import { NextResponse } from "next/server";
import { writeFile } from "fs/promises";
import path from "path";

export async function POST(request: Request) {
try {
const formData = await request.formData();
const file = formData.get("file") as File;

if (!file) {
return NextResponse.json({ error: "未选择文件" }, { status: 400 });
}

const bytes = await file.arrayBuffer();
const buffer = Buffer.from(bytes);

const filename = `${Date.now()}-${file.name}`;
const filepath = path.join(process.cwd(), "public/uploads", filename);

await writeFile(filepath, buffer);

return NextResponse.json({
url: `/uploads/${filename}`,
filename,
});
} catch (error) {
return NextResponse.json({ error: "上传失败" }, { status: 500 });
}
}

分页 API​

// src/app/api/posts/route.ts
import { NextResponse } from "next/server";

export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get("page") || "1");
const limit = parseInt(searchParams.get("limit") || "10");
const skip = (page - 1) * limit;

const [posts, total] = await Promise.all([
db.post.findMany({
skip,
take: limit,
orderBy: { createdAt: "desc" },
}),
db.post.count(),
]);

return NextResponse.json({
data: posts,
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
},
});
}

小结​

本章我们学习了:

  1. Route Handlers 的基本用法
  2. 各种 HTTP 方法的处理
  3. 请求参数和请求体的获取
  4. 响应的构建和 Cookie 操作
  5. 动态路由 API
  6. 实战示例

练习​

  1. 创建一个简单的 CRUD API(增删改查)
  2. 实现一个带验证的用户登录 API
  3. 创建一个分页查询 API
  4. 实现一个文件上传 API