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

## やりたかったこと
Next.jsでHTTPメソッドごとの処理を書きたいが、[Next.js公式](https://nextjs.org/docs/api-routes/introduction)のこれ(以下のコード)はあまりやりたくない
```typescript: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を共通化して各ファイルで使えるようにしたい

## やってみたこと

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

### 被使用側のコード
```typescript: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など)でのリクエストについては、不正なリクエストとしてエラーを返します。
```typescript: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のルーティング部分を抽象化してみました
まだまだ勉強不足なので、もっとこうした方がいいみたいなものがあればコメントで教えてください🙏

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