跳到内容
返回博客

2025 年 2 月 28 日,星期五

使用 Next.js 构建 API

发布者

本指南将介绍如何使用 Next.js 构建 API,包括设置项目、理解 App Router 和 Route Handlers、处理多种 HTTP 方法、实现动态路由、创建可重用的中间件逻辑,以及决定何时启动专用的 API 层。

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.tsroute.js 文件放置在 app/ 目录内的任何位置,而不是 pages/api/*

为什么要切换? App Router 的“Route Handlers”依赖于 Web Platform Request/Response API,而不是 Node.js 特有的 API。这简化了学习过程,减少了摩擦,并帮助您在不同的工具中重用您的知识。

2. 为什么(以及何时)使用 Next.js 构建 API

  1. 面向多个客户端的公共 API

    • 您可以构建一个公共 API,供您的 Next.js Web 应用、独立的移动应用或任何第三方服务使用。例如,您可以在 React 网站和 React Native 移动应用中都从 /api/users 获取数据。
  2. 现有后端的代理

    • 有时您希望隐藏或整合外部 微服务,并将其置于单个端点之后。Next.js Route Handlers 可以充当另一个现有后端的代理或中间层。例如,您可以拦截请求,处理身份验证,转换数据,然后将请求传递给上游 API。
  3. Webhooks 和集成

    • 如果您收到外部回调或 webhooks(例如,来自 Stripe、GitHub、Twilio),您可以使用 Route Handlers 处理它们。
  4. 自定义身份验证

    • 如果您需要会话、令牌或其他身份验证逻辑,您可以在 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 方法的函数。

app/api/users/route.ts
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 方法(GETPOST 等)接收一个标准的 Request 对象,并且您必须返回一个标准的 Response 对象。

4.2 查询参数

app/api/search/route.ts
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

app/api/auth/route.ts
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 的 NextRequestNextResponse

5. 动态路由

要创建动态路径(例如 /api/users/:id),请在您的文件夹结构中使用动态段

app
└── api
    └── users
        └── [id]
            └── route.ts

此文件对应于类似 /api/users/123 的 URL,其中 123 被捕获为一个参数。

app/api/users/[id]/route.ts
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 用作代理或转发层

一个常见的场景是代理现有的后端服务。您可以验证请求、处理日志记录或转换数据,然后再将其发送到远程服务器或后端

app/api/external/route.ts
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

lib/with-auth.ts
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 中

app/api/secret/route.ts
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)

您可以通过设置启用此功能

next.config.ts
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,您可以直接在服务器上获取数据,而无需公开公共端点

app/users/page.tsx
// (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. 整合所有内容

  1. 创建一个新的 Next.js 项目npx create-next-app@latest --api
  2. app/ 目录内添加 Route Handlers(例如,app/api/users/route.ts)。
  3. 在同一文件中导出 HTTP 方法GETPOSTPUTDELETE 等)。
  4. 使用 Web 标准 APIRequest 对象交互并返回 Response
  5. 构建公共 API,如果您需要其他客户端使用您的数据,或者代理后端服务。
  6. 从客户端获取您的新 API 路由(例如,在客户端组件中或使用 fetch('/api/...'))。
  7. 或者,如果 Server Component 可以直接获取数据,则跳过创建 API
  8. 添加共享的“中间件”模式(例如,withAuth())用于身份验证或其他重复逻辑。
  9. 部署到支持 Node.js 的环境以获得服务器功能,或者如果您只需要静态 SPA,则静态导出

结论

使用 Next.js App RouterRoute 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 文件中定义 RequestResponse 类型。

了解更多关于 在 Next.js 中使用 TypeScript 的信息。

身份验证的最佳实践是什么?

在我们的身份验证文档中了解更多信息。