2025年2月28日星期五
使用 Next.js 构建 API
发布者本指南将介绍如何使用 Next.js 构建 API,包括项目设置、了解 App Router 和路由处理器、处理多种 HTTP 方法、实现动态路由、创建可重用中间件逻辑,以及决定何时搭建专门的 API 层。
- 1. 开始
- 2. 为何(以及何时)使用 Next.js 构建 API
- 3. 使用路由处理器创建 API
- 4. 使用 Web API
- 5. 动态路由
- 6. 将 Next.js 用作代理或转发层
- 7. 构建共享“中间件”逻辑
- 8. 部署和“SPA 模式”考量
- 9. 何时跳过创建 API 端点
- 10. 整合所有内容
- 结论
- 常见问题
1. 开始
1.1 创建 Next.js 应用
如果你从头开始,可以使用以下命令创建一个新的 Next.js 项目:
npx create-next-app@latest --api注意:
--api标志会自动在你新项目的app/文件夹中包含一个示例route.ts,演示如何创建 API 端点。
1.2 App Router 与 Pages Router
- Pages Router:过去,Next.js 使用
pages/api/*来构建 API。这种方法依赖于 Node.js 的请求/响应对象以及类似 Express 的 API。 - App Router(默认):Next.js 13 中引入的 App Router 完全采用了 Web 标准的 Request/Response API。现在,你可以在
app/目录中的任何位置放置route.ts或route.js文件,而不是使用pages/api/*。
为什么要切换? App Router 的“路由处理器”依赖于 Web 平台 Request/Response API,而不是 Node.js 特定的 API。这简化了学习,减少了摩擦,并有助于你在不同工具中重用知识。
2. 为何(以及何时)使用 Next.js 构建 API
-
面向多个客户端的公共 API
- 你可以构建一个公共 API,供你的 Next.js Web 应用、独立的移动应用或任何第三方服务使用。例如,你可以在 React 网站和 React Native 移动应用中都从 /api/users 获取数据。
-
现有后端代理
- 有时你希望将外部 微服务 隐藏或整合到单个端点后面。Next.js 路由处理器可以充当代理或中间层,连接到另一个现有后端。例如,你可以拦截请求、处理身份验证、转换数据,然后将请求传递给上游 API。
-
Webhooks 和集成
- 如果你接收外部回调或 Webhooks(例如来自 Stripe、GitHub、Twilio),你可以使用路由处理器来处理它们。
-
自定义身份验证
- 如果你需要会话、令牌或其他身份验证逻辑,可以在 Next.js API 层中存储 cookies、读取头部并响应适当的数据。
注意: 如果你只需要为自己的 Next.js 应用进行服务器端数据获取(并且不需要外部共享数据),那么服务器组件可能足以在渲染期间直接获取数据——不需要单独的 API 层。
3. 使用路由处理器创建 API
3.1 基本文件设置
在 App Router (app/) 中,创建一个表示你的路由的文件夹,并在其中创建一个 route.ts 文件。
例如,要在 /api/users 创建一个端点:
app
└── api
└── users
└── route.ts3.2 单文件中的多个 HTTP 方法
与 Pages Router API 路由(只有一个默认导出)不同,你可以从同一个文件导出表示不同 HTTP 方法的多个函数。
export async function GET(request: Request) {
// For example, fetch data from your DB here
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
return new Response(JSON.stringify(users), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
}
export async function POST(request: Request) {
// Parse the request body
const body = await request.json();
const { name } = body;
// e.g. Insert new user into your DB
const newUser = { id: Date.now(), name };
return new Response(JSON.stringify(newUser), {
status: 201,
headers: { 'Content-Type': 'application/json' }
});
}现在,向 /api/users 发送 GET 请求会返回你的用户列表,而向同一个 URL 发送 POST 请求则会插入一个新用户。
4. 使用 Web API
4.1 直接使用 Request 和 Response
默认情况下,你的路由处理器方法(GET、POST 等)接收一个标准的 Request 对象,并且你必须返回一个标准的 Response 对象。
4.2 查询参数
import { NextRequest } from 'next/server';
export function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const query = searchParams.get('query'); // e.g. `/api/search?query=hello`
return new Response(
JSON.stringify({ result: `You searched for: ${query}` }),
{
headers: { 'Content-Type': 'application/json' },
},
);
}4.3 头部和 Cookies
import { NextRequest } from 'next/server';
import { cookies, headers } from 'next/headers';
export async function GET(request: NextRequest) {
// 1. Using 'next/headers' helpers
const cookieStore = await cookies();
const token = cookieStore.get('token');
const headersList = await headers();
const referer = headersList.get('referer');
// 2. Using the standard Web APIs
const userAgent = request.headers.get('user-agent');
return new Response(JSON.stringify({ token, referer, userAgent }), {
headers: { 'Content-Type': 'application/json' },
});
}cookies() 和 headers() 函数在您计划在 Next.js 中的其他服务器端代码中重用共享逻辑时会很有帮助。您会注意到 Next.js 还提供了 NextRequest 和 NextResponse,它们扩展了基本的 Web API。
5. 动态路由
要创建动态路径(例如 /api/users/:id),请在您的文件夹结构中使用 动态段:
app
└── api
└── users
└── [id]
└── route.ts此文件对应于像 /api/users/123 这样的 URL,其中 123 被捕获为参数。
import { NextRequest } from 'next/server';
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
const id = (await params).id;
// e.g. Query a database for user with ID `id`
return new Response(JSON.stringify({ id, name: `User ${id}` }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
const id = (await params).id;
// e.g. Delete user with ID `id` in DB
return new Response(null, { status: 204 });
}在这里,params.id 为您提供了动态段。
6. 将 Next.js 用作代理或转发层
一个常见场景是代理现有后端服务。您可以在将请求发送到远程服务器或后端之前,对请求进行身份验证、处理日志记录或转换数据。
import { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const response = await fetch('https://example.com/api/data', {
// Optional: forward some headers, add auth tokens, etc.
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
});
// Transform or forward the response
const data = await response.json();
const transformed = { ...data, source: 'proxied-through-nextjs' };
return new Response(JSON.stringify(transformed), {
headers: { 'Content-Type': 'application/json' },
});
}现在你的客户端只需要调用 /api/external,Next.js 会处理其余部分。这有时也称为“前端后端”或 BFF。
7. 构建共享“中间件”逻辑
如果你想将相同的逻辑(例如身份验证检查、日志记录)应用于多个路由处理器,你可以创建可重用函数来包装你的处理器。
import { NextRequest } from 'next/server';
type Handler = (req: NextRequest, context?: any) => Promise<Response>;
export function withAuth(handler: Handler): Handler {
return async (req, context) => {
const token = req.cookies.get('token')?.value;
if (!token) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
// If authenticated, call the original handler
return handler(req, context);
};
}然后在你的路由处理器中
import { NextRequest } from 'next/server';
import { withAuth } from '@/lib/with-auth';
async function secretGET(request: NextRequest) {
return new Response(JSON.stringify({ secret: 'Here be dragons' }), {
headers: { 'Content-Type': 'application/json' },
});
}
export const GET = withAuth(secretGET);8. 部署和“SPA 模式”考量
8.1 标准 Node.js 部署
使用 next start 的标准 Next.js 服务器部署允许您使用路由处理器、服务器组件、中间件等功能——同时利用动态的、请求时信息。
无需额外配置。有关更多详细信息,请参阅部署。
8.2 SPA/静态导出
Next.js 还支持将您的整个网站输出为静态单页应用程序 (SPA)。
您可以通过设置以下内容来启用此功能:
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'export',
};
export default nextConfig;在静态导出模式下,Next.js 将生成纯静态 HTML、CSS 和 JS。您无法运行服务器端代码(例如 API 端点)。如果您仍然需要 API,则必须单独托管它(例如,独立的 Node.js 服务器)。
注意
- 如果 GET 路由处理器不依赖于动态请求数据,它们可以静态导出。它们会成为您的输出文件夹中的静态文件。
- 所有其他服务器功能(动态请求、重写 cookies 等)在纯 SPA 导出中不支持。
8.3 在 Vercel 上部署 API
如果您正在将 Next.js 应用程序部署到 Vercel,我们提供了一份部署 API 的指南。这包括其他 Vercel 功能,例如通过 Vercel 防火墙进行的程序化速率限制。Vercel 还提供了Cron Jobs,这通常是 API 方法所需要的。
9. 何时跳过创建 API 端点
通过 App Router 的 React Server Components,您可以在服务器上直接获取数据,而无需暴露公共端点。
// (Server Component)
export default async function UsersPage() {
// This fetch runs on the server (no client-side code needed here)
const res = await fetch('https://api.example.com/users');
const data = await res.json();
return (
<main>
<h1>Users</h1>
<ul>
{data.map((user: any) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</main>
);
}如果您的数据仅在您的 Next.js 应用内部使用,您可能根本不需要公共 API。
10. 整合所有内容
- 创建一个新的 Next.js 项目:
npx create-next-app@latest --api。 - 在
app/目录中添加路由处理器(例如,app/api/users/route.ts)。 - 在同一个文件中导出 HTTP 方法(
GET、POST、PUT、DELETE等)。 - 使用 Web 标准 API 与
Request对象交互并返回Response。 - 如果您需要其他客户端使用您的数据,或者需要代理后端服务,则构建一个公共 API。
- 从客户端获取您新的 API 路由(例如,在客户端组件中或使用
fetch('/api/...'))。 - 或者,如果服务器组件可以直接获取数据,则完全跳过创建 API。
- 为身份验证或其他重复逻辑添加共享“中间件”模式(例如,
withAuth())。 - 部署到支持 Node.js 的环境以使用服务器功能,或者如果您只需要静态 SPA,则进行静态导出。
结论
使用 Next.js 的 App Router 和 路由处理器 为您提供了一种灵活、现代的方式来直接构建拥抱 Web 平台 的 API。您可以:
- 创建完整的公共 API,供 Web、移动或第三方客户端共享。
- 代理并自定义对现有外部服务的调用。
- 实现可重用的“中间件”层,用于身份验证、日志记录或任何重复逻辑。
- 使用
[id]段文件夹结构动态路由请求。
常见问题
Server Actions 是什么?
您可以将服务器动作 (Server Actions) 视为可以从客户端调用的自动生成的 POST API 路由。
它们专为变异操作而设计,例如创建、更新或删除数据。您可以像调用普通的 JavaScript 函数一样调用服务器动作,而不是对已定义的 API 路由进行明确的 fetch 请求。
虽然仍然存在网络请求,但您无需明确管理它。URL 路径是自动生成的并加密的,因此您无法在浏览器中手动访问像 /api/users 这样的路由。
如果您计划同时使用服务器动作和公开公共 API,我们建议将核心逻辑移动到数据访问层,并从服务器动作和 API 路由中调用相同的逻辑。
我可以在路由处理器中使用 TypeScript 吗?
是的,您可以在路由处理器中使用 TypeScript。例如,在您的 route 文件中定义 Request 和 Response 类型。
了解更多关于Next.js 和 TypeScript 的信息。
身份验证的最佳实践是什么?
在我们的身份验证文档中了解更多信息。