中文

探索 Next.js API 路由,为您的 React 应用解锁全栈开发能力。了解其开发模式、最佳实践与部署策略。

Next.js API 路由:全栈开发模式

Next.js 通过提供一个用于构建高性能、可扩展 Web 应用的强大框架,彻底改变了 React 开发。其关键特性之一是 API 路由 (API Routes),它使开发者能够直接在 Next.js 项目中创建后端功能。这种方法简化了开发流程,简化了部署,并解锁了强大的全栈能力。

什么是 Next.js API 路由?

Next.js API 路由是直接编写在 /pages/api 目录下的无服务器函数。此目录中的每个文件都会成为一个 API 端点,自动将 HTTP 请求路由到其对应的函数。这消除了对独立后端服务器的需求,简化了您的应用架构并减少了运营开销。

您可以将它们视为存在于 Next.js 应用内部的微型无服务器函数。它们响应 GET、POST、PUT、DELETE 等 HTTP 请求,并能与数据库、外部 API 及其他服务器端资源进行交互。至关重要的是,它们只在服务器上运行,而不在用户的浏览器中运行,从而确保了 API 密钥等敏感数据的安全。

API 路由的主要优势

API 路由入门

在 Next.js 中创建 API 路由非常简单。只需在 /pages/api 目录下创建一个新文件。文件名将决定路由的路径。例如,创建一个名为 /pages/api/hello.js 的文件将创建一个可通过 /api/hello 访问的 API 端点。

示例:一个简单的问候 API

以下是一个返回 JSON 响应的基本 API 路由示例:


// pages/api/hello.js

export default function handler(req, res) {
  res.status(200).json({ message: 'Hello from Next.js API Route!' });
}

此代码定义了一个异步函数 handler,它接收两个参数:

该函数将 HTTP 状态码设置为 200 (OK),并返回一个带有消息的 JSON 响应。

处理不同的 HTTP 方法

您可以通过检查 req.method 属性来处理 API 路由中的不同 HTTP 方法(GET、POST、PUT、DELETE 等)。这使您可以轻松创建 RESTful API。


// pages/api/todos.js

export default async function handler(req, res) {
  if (req.method === 'GET') {
    // 从数据库获取所有待办事项
    const todos = await fetchTodos();
    res.status(200).json(todos);
  } else if (req.method === 'POST') {
    // 创建一个新的待办事项
    const newTodo = await createTodo(req.body);
    res.status(201).json(newTodo);
  } else {
    // 处理不支持的方法
    res.status(405).json({ message: 'Method Not Allowed' });
  }
}

此示例演示了如何为一个假设的 /api/todos 端点处理 GET 和 POST 请求。它还包括了对不支持方法的错误处理。

使用 API 路由的全栈开发模式

Next.js API 路由支持多种全栈开发模式。以下是一些常见的用例:

1. 数据获取与操作

API 路由可用于从数据库、外部 API 或其他数据源获取数据。它们也可用于操作数据,例如创建、更新或删除记录。

示例:从数据库获取用户数据


// pages/api/users/[id].js
import { query } from '../../../lib/db';

export default async function handler(req, res) {
  const { id } = req.query;

  try {
    const results = await query(
      'SELECT * FROM users WHERE id = ?',
      [id]
    );

    if (results.length === 0) {
      return res.status(404).json({ message: 'User not found' });
    }

    res.status(200).json(results[0]);
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Internal Server Error' });
  }
}

此示例根据 URL 中提供的用户 ID 从数据库中获取用户数据。它使用一个数据库查询库(假设在 lib/db 中)与数据库进行交互。请注意,这里使用了参数化查询以防止 SQL 注入漏洞。

2. 身份验证与授权

API 路由可用于实现身份验证和授权逻辑。您可以使用它们来验证用户凭据、生成 JWT 令牌以及保护敏感资源。

示例:用户身份验证


// pages/api/login.js
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { query } from '../../lib/db';

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const { email, password } = req.body;

    try {
      const results = await query(
        'SELECT * FROM users WHERE email = ?',
        [email]
      );

      if (results.length === 0) {
        return res.status(401).json({ message: 'Invalid credentials' });
      }

      const user = results[0];

      const passwordMatch = await bcrypt.compare(password, user.password);

      if (!passwordMatch) {
        return res.status(401).json({ message: 'Invalid credentials' });
      }

      const token = jwt.sign(
        { userId: user.id, email: user.email },
        process.env.JWT_SECRET,
        { expiresIn: '1h' }
      );

      res.status(200).json({ token });
    } catch (error) {
      console.error(error);
      res.status(500).json({ message: 'Internal Server Error' });
    }
  } else {
    res.status(405).json({ message: 'Method Not Allowed' });
  }
}

此示例通过将提供的密码与数据库中存储的哈希密码进行比较来验证用户身份。如果凭据有效,它会生成一个 JWT 令牌并返回给客户端。客户端随后可以使用此令牌来验证后续请求。

3. 表单处理与数据提交

API 路由可用于处理表单提交和处理从客户端发送的数据。这对于创建联系表单、注册表单和其他交互式元素非常有用。

示例:联系表单提交


// pages/api/contact.js
import { sendEmail } from '../../lib/email';

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const { name, email, message } = req.body;

    try {
      await sendEmail({
        to: 'admin@example.com',
        subject: 'New Contact Form Submission',
        text: `Name: ${name}\nEmail: ${email}\nMessage: ${message}`,
      });

      res.status(200).json({ message: 'Email sent successfully' });
    } catch (error) {
      console.error(error);
      res.status(500).json({ message: 'Failed to send email' });
    }
  } else {
    res.status(405).json({ message: 'Method Not Allowed' });
  }
}

此示例通过向管理员发送电子邮件来处理联系表单的提交。它使用一个电子邮件发送库(假设在 lib/email 中)来发送邮件。您应将 admin@example.com 替换为实际的收件人电子邮件地址。

4. Webhook 与事件处理

API 路由可用于处理 Webhook 并响应来自外部服务的事件。这使您可以将 Next.js 应用与其他平台集成并实现任务自动化。

示例:处理 Stripe Webhook


// pages/api/stripe-webhook.js
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

export const config = {
  api: {
    bodyParser: false, // 禁用默认的 body 解析
  },
};

async function buffer(req) {
  const chunks = [];
  for await (const chunk of req) {
    chunks.push(chunk);
  }
  return Buffer.concat(chunks).toString();
}

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const sig = req.headers['stripe-signature'];

    let event;

    try {
      const buf = await buffer(req);
      event = stripe.webhooks.constructEvent(buf, sig, process.env.STRIPE_WEBHOOK_SECRET);
    } catch (err) {
      console.log(`Webhook Error: ${err.message}`);
      res.status(400).send(`Webhook Error: ${err.message}`);
      return;
    }

    // 处理事件
    switch (event.type) {
      case 'payment_intent.succeeded':
        const paymentIntent = event.data.object;
        console.log(`PaymentIntent for ${paymentIntent.amount} was successful!`);
        // 然后定义并调用一个方法来处理成功的支付意图。
        // handlePaymentIntentSucceeded(paymentIntent);
        break;
      case 'payment_method.attached':
        const paymentMethod = event.data.object;
        // 然后定义并调用一个方法来处理成功附加的支付方式。
        // handlePaymentMethodAttached(paymentMethod);
        break;
      default:
        // 意外的事件类型
        console.log(`Unhandled event type ${event.type}.`);
    }

    // 返回 200 响应以确认收到事件
    res.status(200).json({ received: true });
  } else {
    res.setHeader('Allow', 'POST');
    res.status(405).end('Method Not Allowed');
  }
}

此示例通过验证签名和处理事件数据来处理 Stripe Webhook。它禁用了默认的 body 解析器,并使用一个自定义的 buffer 函数来读取原始请求体。禁用默认的 body 解析器至关重要,因为 Stripe 需要原始请求体来进行签名验证。请记得在您的 Stripe 仪表板中配置 Webhook 端点,并设置 STRIPE_WEBHOOK_SECRET 环境变量。

API 路由的最佳实践

为确保您的 API 路由的质量和可维护性,请遵循以下最佳实践:

1. 代码模块化

避免编写庞大、单一的 API 路由。相反,应将代码分解为更小、可重用的模块。这使您的代码更易于理解、测试和维护。

2. 实现错误处理

在您的 API 路由中妥善处理错误。使用 try...catch 块来捕获异常,并向客户端返回适当的错误响应。记录错误有助于调试和监控。

3. 验证输入数据

始终验证来自客户端的输入数据,以防止安全漏洞并确保数据完整性。使用像 Joi 或 Yup 这样的验证库来定义验证模式并强制执行数据约束。

4. 保护敏感数据

将敏感数据(如 API 密钥和数据库凭据)存储在环境变量中。切勿将敏感数据提交到您的代码仓库。

5. 实施速率限制

通过实施速率限制来保护您的 API 路由免受滥用。这可以限制客户端在给定时间段内可以发出的请求数量。使用像 express-rate-limitlimiter 这样的速率限制库。

6. 保护 API 密钥

不要在客户端代码中直接暴露 API 密钥。始终通过您的 API 路由代理请求,以保护您的 API 密钥免遭未经授权的访问。将 API 密钥安全地存储在服务器的环境变量中。

7. 使用环境变量

避免在代码中硬编码配置值。应使用环境变量来存储配置设置。这使得在不同环境(开发、预发布、生产)中管理您的应用变得更加容易。

8. 日志与监控

实施日志和监控来跟踪您的 API 路由的性能。记录重要事件,如错误、警告和成功请求。使用监控工具来跟踪请求延迟、错误率和资源使用等指标。像 Sentry、Datadog 或 New Relic 这样的服务会很有帮助。

部署注意事项

Next.js API 路由旨在部署于无服务器平台。热门的部署选项包括:

当您使用 API 路由部署 Next.js 应用时,请确保在部署平台上正确配置了环境变量。此外,还需考虑无服务器函数的冷启动时间,这可能会影响您的 API 路由的初始响应时间。优化代码和使用预置并发等技术有助于缓解冷启动问题。

结论

Next.js API 路由为使用 React 构建全栈应用提供了一种强大而便捷的方式。通过利用无服务器函数,您可以简化开发、减少运营开销并提升应用性能。遵循本文概述的最佳实践,您可以创建出健壮且可维护的 API 路由,为您的 Next.js 应用提供动力。

无论您是在构建一个简单的联系表单还是一个复杂的电子商务平台,Next.js API 路由都能帮助您简化开发流程并提供卓越的用户体验。

进一步学习