2025 年 2 月 28 日,星期五
使用 Next.js 构建 API
发布者本指南将介绍如何使用 Next.js 构建 API,包括设置项目、理解 App Router 和 Route Handlers、处理多种 HTTP 方法、实现动态路由、创建可重用的中间件逻辑,以及决定何时启动专用的 API 层。
- 1. 入门
- 2. 为什么(以及何时)使用 Next.js 构建 API
- 3. 使用 Route Handlers 创建 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。现在,您可以将
route.ts
或route.js
文件放置在app/
目录内的任何位置,而不是pages/api/*
。
为什么要切换? App Router 的“Route Handlers”依赖于 Web Platform Request/Response API,而不是 Node.js 特有的 API。这简化了学习过程,减少了摩擦,并帮助您在不同的工具中重用您的知识。
2. 为什么(以及何时)使用 Next.js 构建 API
-
面向多个客户端的公共 API
- 您可以构建一个公共 API,供您的 Next.js Web 应用、独立的移动应用或任何第三方服务使用。例如,您可以在 React 网站和 React Native 移动应用中都从
/api/users
获取数据。
- 您可以构建一个公共 API,供您的 Next.js Web 应用、独立的移动应用或任何第三方服务使用。例如,您可以在 React 网站和 React Native 移动应用中都从
-
现有后端的代理
- 有时您希望隐藏或整合外部 微服务,并将其置于单个端点之后。Next.js Route Handlers 可以充当另一个现有后端的代理或中间层。例如,您可以拦截请求,处理身份验证,转换数据,然后将请求传递给上游 API。
-
Webhooks 和集成
- 如果您收到外部回调或 webhooks(例如,来自 Stripe、GitHub、Twilio),您可以使用 Route Handlers 处理它们。
-
自定义身份验证
- 如果您需要会话、令牌或其他身份验证逻辑,您可以在 Next.js API 层中存储 Cookie、读取标头并响应适当的数据。
注意: 如果您只需要为自己的 Next.js 应用进行服务器端数据获取(并且您不需要在外部共享该数据),则 Server Components 可能足以在渲染期间直接获取数据——无需单独的 API 层。
3. 使用 Route Handlers 创建 API
3.1 基本文件设置
在 App Router (app/
) 中,创建一个代表您的路由的文件夹,并在其中创建一个 route.ts
文件。
例如,要在 /api/users
创建一个端点
app
└── api
└── users
└── route.ts
3.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
默认情况下,您的 Route Handler 方法(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 标头和 Cookie
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' },
});
}
如果您计划在 Next.js 中的其他服务器端代码中重用共享逻辑,则 cookies()
和 headers()
函数可能会很有用。您会注意到 Next.js 还提供了扩展了基本 Web API 的 NextRequest
和 NextResponse
。
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. 构建共享的“中间件”逻辑
如果您想在多个 Route Handlers 中应用相同的逻辑(例如,身份验证检查、日志记录),您可以创建可重用的函数来包装您的 handlers
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);
};
}
然后在您的 Route Handler 中
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 服务器部署使您可以使用 Route Handlers、Server Components、Middleware 等功能——同时利用动态的请求时信息。
无需额外的配置。请参阅部署了解更多详细信息。
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 Route Handlers 如果不依赖于动态请求数据,可以静态导出。它们将成为您的 out 文件夹中的静态文件。
- 所有其他服务器功能(动态请求、重写 Cookie 等)在纯 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/
目录内添加 Route Handlers(例如,app/api/users/route.ts
)。 - 在同一文件中导出 HTTP 方法(
GET
、POST
、PUT
、DELETE
等)。 - 使用 Web 标准 API 与
Request
对象交互并返回Response
。 - 构建公共 API,如果您需要其他客户端使用您的数据,或者代理后端服务。
- 从客户端获取您的新 API 路由(例如,在客户端组件中或使用
fetch('/api/...')
)。 - 或者,如果 Server Component 可以直接获取数据,则跳过创建 API。
- 添加共享的“中间件”模式(例如,
withAuth()
)用于身份验证或其他重复逻辑。 - 部署到支持 Node.js 的环境以获得服务器功能,或者如果您只需要静态 SPA,则静态导出。
结论
使用 Next.js App Router 和 Route Handlers 为您提供了一种灵活、现代的方式来构建直接采用 Web Platform 的 API。您可以
- 创建完整的公共 API,供 Web、移动或第三方客户端共享。
- 代理和自定义对现有外部服务的调用。
- 实现可重用的“中间件”层,用于身份验证、日志记录或任何重复逻辑。
- 使用
[id]
段文件夹结构动态路由请求。
常见问题解答
Server Actions 怎么样?
您可以将 Server Actions 视为可以从客户端调用的自动生成的 POST
API 路由。
它们专为突变操作而设计,例如创建、更新或删除数据。您可以像调用普通的 JavaScript 函数一样调用 Server Action,而不是显式地 fetch
到定义的 API 路由。
虽然仍然会发生网络请求,但您无需显式管理它。URL 路径是自动生成的并且是加密的,因此您无法在浏览器中手动访问类似 /api/users
的路由。
如果您计划使用 Server Actions 并公开公共 API,我们建议将核心逻辑移动到数据访问层,并从 Server Action 和 API 路由调用相同的逻辑。
我可以在 Route Handlers 中使用 TypeScript 吗?
是的,您可以在 Route Handlers 中使用 TypeScript。例如,在您的 route
文件中定义 Request
和 Response
类型。
了解更多关于 在 Next.js 中使用 TypeScript 的信息。
身份验证的最佳实践是什么?
在我们的身份验证文档中了解更多信息。