如何处理 Next.js api 中的错误?

mar*_*vic 5 javascript rest express next.js

在 Next.js api 处理程序中组织代码的最佳实践是什么?我看了这个视频,他将所有 REST 路线放在两个文件中:

  1. pages/api/users/index.ts处理所有不需要的id操作GET /api/users - get all usersPOST pages/api/users - create a new user

  2. pages/api/users/[id].ts处理所有需要idso GET api/users/1 - get user by idPUT /api/users/1 - update user和 的操作DELETE /api/users/1 - delete a user

这意味着大量代码将进入 2 个文件并由一条switch case语句处理。所有这些代码应该如何组织?

每个case语句都应该有自己的try catch块来处理数据库调用,这会大量重复,我可以将单个语句try catch全部封装起来switch,但这会包装很多不必要的代码,也许每个语句都case需要不同的处理?我可以将 single 放在try catch高阶函数中并用它包装每个 case 块,但我不确定这也很好吗?

另外,稍后我将需要使用中间件来保护一些路由withProtectedwithRole但这并不容易,因为现在多个 api 是在单个处理程序调用中处理的。

组织这个活动的最佳方式是什么?是否已经存在很好的完整示例?


// pages/api/users/index.ts

import { NextApiRequest, NextApiResponse } from 'next';
import { hash } from 'bcryptjs';
import prisma from 'lib/prisma';

/**
 * POST /api/users
 * Required fields in body: name, username, email, password
 */

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
): Promise<void> {
  const { method, body } = req;

  switch (method) {
    case 'GET':
      // get all
      // try catch again?
      const users = await prisma.user.findMany();
      res.status(200).json({ users });

      break;
    case 'POST':
      // create
      try {
        const { name, username, email, password: _password } = body;

        // todo: validate...

        const _user = await prisma.user.findFirst({
          where: { email },
        });

        if (_user)
          throw new Error(`The user with email: ${email} already exists.`);

        const password = await hash(_password, 10);
        const user = await prisma.user.create({
          data: {
            name,
            username,
            email,
            password,
          },
        });

        res.status(201).json({ user });
      } catch (error) {
        res.status(500).json({ error });
      }

      break;
    default:
      res.setHeader('Allow', ['GET', 'POST']);
      res.status(405).end(`Method ${method} Not Allowed`);
  }
}

Run Code Online (Sandbox Code Playgroud)

Ard*_*nar 1

这就是我做事的方式。它还涵盖 method not allowed

我的项目中的工作代码示例

src/somewhere/globalAPICall.js

/**
 * 
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {{[key: string]: () => Promise<void>}} actions 
 */
export default async function globalAPICall(req, res, actions) {
    try {
      const method = req.method
      // check an action exists with request.method else throw method not allowed
      if (!Object.keys(actions).includes(method)) {
        console.log('method not allowed')
        throw new MyError('Method not allowed', 405)
      }
      // run the action matching the request.method
      await actions[method]()
    } catch(err) {
      if (err instanceof MyError) {
        res.status(err.code).send(err.message);
      } else {
        res.status(500).send("Internal server error");
      }
    }
}
Run Code Online (Sandbox Code Playgroud)

src/pages/api/users.js


/**
 *
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 */
export default async function handler(req, res) {

    async function POST() {
      const { email, password, username } = req.body;
      if (!username) {
        throw new MyError('Username required', 400)
      }
      await CreateUser(email, password, username)
      res.status(201).send();
    }

    async function GET() {
      const result = await ListUsers()
      res.status(200).json(result);
    }


    await globalAPICall.js(req, res, {POST, GET})

}
Run Code Online (Sandbox Code Playgroud)

  • MyError只是一个Error类型,你可以使用JS的基本错误类型,或者创建自己的错误类型,都没有关系。而且,很抱歉回复晚了,我因新冠肺炎失去了爷爷,所以最近没有时间去检查。 (2认同)