Next.jsでAPIのエンドポイントを組む際はnext-connectを使うと良いよ

2021-12-09

Next.js で API のエンドポイントを組むの場合、なかなかしっくり来る組み方に出会えていませんでした。

そんな中 [next-connect] を使ってみると、かなりしっくり来る組み方ができたので紹介をば。

まずは Next.js の 公式ドキュメント に沿ってみるとこんな感じになります。

function handler(req, res) {
  const { method } = req;
  const { idToken } = nookies({ req });

  // idToken のあれこれ

  if (method === "GET") {
    // GET のあれこれ
    res.status(200).json({ hoge: "hoge" });
  } else if (method === "POST") {
    // POST のあれこれ
    res.status(200).json({ fuga: "fuga" });
  }
}

export default handler;

上記を見ると悪くなさそうに見えるのですが、TypeScript となるとそうもいきません。

import type { NextApiRequest, NextApiResponse } from "next";

type GetData = {
  hoge: string;
};

type PostData = {
  fuga: string;
};

function handler(
  req: NextApiRequest,
  res: NextApiResponse<GetData | PostData>,
) {
  const { idToken } = parseCookies({ req });
  const { method } = req;

  // idToken のあれこれ

  if (method === "GET") {
    // GET のあれこれ
    res.status(200).json({ hoge: "hoge" });
  } else if (method === "POST") {
    // POST のあれこれ
    res.status(200).json({ fuga: "fuga" });
  }
}

export default handler;

結構ごちゃついてくる印象がありますが、いかがでしょうか。

で、これを next-connect を使って書き換えるとこんな感じになります。

import type { NextApiRequest, NextApiResponse } from "next";
import nc from "next-connect";

type GetData = {
  hoge: string;
};

type PostData = {
  fuga: string;
};

const handler = nc<NextApiRequest, NextApiResponse<GetData | PostData>>().use(
  // ここを middleware として共通化しても良い
  (req, _, next) => {
    const { idToken } = parseCookies({ req });

    // idToken のあれこれ

    next();
  },
);

type ExtendedGetRequest = Record<string, never>;

type ExtendedGetResponse = {
  json(body: GetData): void;
};

handler.get<ExtendedGetRequest, ExtendedGetResponse>(
  // 型をつけなくても良い
  async (_, res: NextApiResponse<GetData>) => {
    // GET のあれこれ
    res.status(200).json({ hoge: "hoge" });
  },
);

type ExtendedPostRequest = Record<string, never>;

type ExtendedPostResponse = {
  json(body: PostData): void;
};

handler.post<ExtendedPostRequest, ExtendedPostResponse>(
  // 型をつけなくても良い
  async (_, res: NextApiResponse<PostData>) => {
    // POST のあれこれ
    res.status(200).json({ fuga: "fuga" });
  },
);

export default handler;

もちろん getpost の処理をそのままチェーンで繋ぐことも可能です。

で、next-connect のもっとも大きいメリットが、use の引数を middleware として共通化することも可能な点です。

他にもエラーハンドリングの書きっぷりも強いですし、共通化にも対応しているのがすごいですね。

さらに getServerSideProps でも呼び出し可能なので、middleware をすべて共通化すると良い感じです。

ということで、今後はこれを使って書いていこうかなーと思います。