LoginSignup
0
3

More than 1 year has passed since last update.

【Next.js】HTTP methods ハンドリングを抽象化してみる

Posted at

やりたかったこと

Next.jsでHTTPメソッドごとの処理を書きたいが、Next.js公式のこれ(以下のコード)はあまりやりたくない

index.ts
export default function handler(req, res) {
  if (req.method === 'POST') {
    // Process a POST request
  } else {
    // Handle any other HTTP method
  }
}

なぜなら...

  • 各ファイルでif (req.methods === 'POST') {みたいな分岐を書くのが面倒。
  • 各ファイルでレスポンス操作 (res.status('OK').json(response)みたいなこと)するのが面倒。
  • エラーハンドリングは共通にしたい。

というわけで、API routesを共通化して各ファイルで使えるようにしたい

やってみたこと

とりあえず共通化するモジュールみたいなのを作ってみました

被使用側のコード

httpMethodHandler.ts
import { NextApiRequest, NextApiResponse } from "next";
import CustomError from "@server/domain/entity/error";
import { StatusCodes, Codes } from "@constants/errors/statusCode";

interface InputMethods {
  get?: () => Promise<any>;
  post?: () => Promise<any>;
  put?: () => Promise<any>;
  delete?: () => Promise<any>;
}

export interface ResponseData {
  data: any;
  error: any;
}

export class HttpMethodHandler {
  get: (() => Promise<any>) | undefined;
  post: (() => Promise<any>) | undefined;
  put: (() => Promise<any>) | undefined;
  delete: (() => Promise<any>) | undefined;

  constructor(params: InputMethods) {
    this.get = params.get;
    this.post = params.post;
    this.put = params.put;
    this.delete = params.delete;
  }

  async execute(req: NextApiRequest, res: NextApiResponse) {
    const response: ResponseData = {
      data: undefined,
      error: undefined,
    };
    try {
      if (req.method === "GET" && !!this.get) {
        response.data = await this.get();
      } else if (req.method === "POST" && !!this.post) {
        response.data = await this.post();
      } else if (req.method === "PUT" && !!this.put) {
        response.data = await this.put();
      } else if (req.method === "DELETE" && !!this.delete) {
        response.data = await this.delete();
      } else {
        throw new CustomError({
          statusCode: StatusCodes.INTERNAL_SERVER_ERROR,
          message: "指定されたリクエストは無効です。",
          code: Codes.INTERNAL_SERVER_ERROR,
        });
      }
      res.status(StatusCodes.OK).json(response);
    } catch (e) {
      console.error(e);
      if (e instanceof CustomError) {
        response.error = {
          statusCode: e.statusCode,
          code: e.code,
          message: e.message,
        };
      } else {
        response.error = {
          statusCode: StatusCodes.INTERNAL_SERVER_ERROR,
          code: Codes.INTERNAL_SERVER_ERROR,
          message: "予期せぬエラーが発生しました。",
        };
      }
      res
        .status(
          response.error
            ? response.error.statusCode
            : StatusCodes.INTERNAL_SERVER_ERROR
        )
        .json(response);
    }
  }
}

使用側のコード

使うときは以下の形で、Httpメソッドごとに関数を入れます。
関数が指定されていないメソッド(以下ファイルで言うとputなど)でのリクエストについては、不正なリクエストとしてエラーを返します。

orders.ts
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  const methodHandler = new HttpMethodHandler({
    get: async () => {
      return await getAllOrders();
    },
    post: async () => {
      return await registerOrder(req.body.order);
    }
  });

  await methodHandler.execute(req, res);
}

だいぶスッキリした気がします 👀

あまり複雑なことはしていないのでポイントというポイントはないですが、

  • 関数が指定されていないメソッドでリクエストがあった場合の挙動
  • 想定外の関数名が入力されないようにするため、最低限の型定義

あたりを意識してみました

まとめ

今回はNext.jsのルーティング部分を抽象化してみました
まだまだ勉強不足なので、もっとこうした方がいいみたいなものがあればコメントで教えてください🙏

最後までご覧いただきありがとうございました

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3