😀

Next.jsのRoute Handlers入門 ─ App RouterでAPIを定義する基本

に公開

この記事の対象者

・Route Handlersを使ったことがない人
・Next.js初心者〜中級者
・実務の導入を考えていて、概要をざっくり知りたい方

Route Handlersとは

Next.js13からApp Router専用に導入されたAPI実装の仕組みです。
従来のPages Routerでは pages/api 以下にAPI Routesを置いてサーバー側の処理を実装していましたが、App Routerでは app/api ディレクトリに route.ts (または route.js) を配置して同じことを実現します。

公式ドキュメントでは次のように説明されています:

Good to know: Route Handlers are only available inside the app directory. They are the equivalent of API Routes inside the pages directory meaning you do not need to use API Routes and Route Handlers together.
(Route Handlersはappディレクトリ内でのみ利用可能です。これはpagesディレクトリ内のAPI Routesと同等のものであり、API RoutesとRoute Handlersを併用する必要はありません。)

つまり 「App RouterにおけるAPI Routes」という位置づけであり、フォーム送信や外部サービス連携、Webhook受信などに利用されます。

基本的な書き方(最小のコード)

Route Handlersを使うと、独自のAPIエンドポイントを簡単に作成できます。route.js(またはroute.ts)ファイルで定義します。

route.js
// src/app/api/hello/route.js
export async function GET() {
  return new Response(JSON.stringify({ message: "Hello" }), {
    headers: { "Content-Type": "application/json" },
  });
}

Route Handlersでは、GET、POST、PUT、PATCH、DELETE、HEAD、OPTIONSなど、fetchで使えるHTTPメソッドはほぼ全部使うことができます。これは、API側もNode.js独自仕様ではなくWeb標準(Request/Responseオブジェクト)を使う方向に統一したためです。

このとき、src/app/api/hello/page.jsxのようにすることはできません。page.jsxlayout.jsxとroute.js`を一緒に置くとエラーが出てしまいます。同じディレクトリに置くのは避けましょう。

NextResponseを使った簡略書き

また、Next.jsは便利に扱えるように拡張したラッパーとしてNextResponseを用意しており、これを使うと先ほどのコードは以下のように書けます。

route.js
// src/app/api/hello/route.js
import { NextResponse } from "next/server";

export async function GET() {
  return NextResponse.json({ message: "Hello, world!" });
}

先ほどは素のFetch APIと同じ書き方で、自分でJSON.stringifyやヘッダーを指定する必要がありました。
しか多くの場合、NextResponseで書けてしまうようです。NextResponseを使うことで、Next.js特有の機能 (cookies, rewrite, redirect) を扱うことができますし、json()のように簡潔に書くことができます。

return NextResponse.redirect("https://example.com");

実際に使うケース

Route Handlersは、フォーム送信やクライアントからのデータ受け取りに便利です。例えば「入力フォームからサーバーにメッセージを送信し、レスポンスを返す」という処理を実装してみましょう。

サーバー側:Route Handlers

import { NextResponse } from "next/server";

export async function POST(req) {
  const { message } = await req.json();
  console.log("受信メッセージ:", message);

  return NextResponse.json({ success: true, message });
}

クライアント側: フォーム送信

"use client";
import React, { useState } from "react";

const Page = () => {
  const [message, setMessage] = useState("");
  const [status, setStatus] = useState("");

  async function handleSubmit(e) {
    e.preventDefault();

    const res = await fetch("/api/post/", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ message }),
    });

    if (res.ok) {
      const data = await res.json();
      setStatus(`送信成功: ${data.message}`);
    } else {
      setStatus("送信失敗");
    }
  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <label htmlFor="message">メッセージ</label>
        <input
          type="text"
          name="message"
          onChange={(e) => setMessage(e.target.value)}
        />
        <button type="submit">送信</button>
      </form>
      {status && <p>{status}</p>}
    </div>
  );
};

export default Page;

値を入れて送信すると...

ターミナルに表示される!

今回の例では、フォームで送信した値をターミナルで表示するだけですが、route.jsにデータベースへ保存するコードを追加することでさらに実務に近い形になります。

最後に

今回はRoute Handlersに関してざっくり調べてみました。
公式のブログやAIを活用して調べてみると、Next.jsは将来的にAPIを定義する必要すらもなくしていくらしいです。なのでサーバーアクションはRoute handlersのさらに進化版なのかなとも考えてみました。
また、Route Handlersの使い時がいまいちわからなかったです。
例えば、Route HandlersはAPIキーを隠したりCORSの問題を回避するのに役立ちますが、サーバーアクションで代用できるような気がしました。また、APIキーを使わないケースなら、サーバーコンポーネントでfetch用のユーティリティ関数を定義して、クライアントコンポーネントなどで使いまわすことだってできます。実際に僕は実務で後者のようにしています。
なので次回は「Route Handlers vs サーバーアクション」をテーマに、それぞれの使い分けなどをメインに深掘っていきたいと思います。

参考
・Next.js公式ドキュメント:https://nextjs.org/docs/app/api-reference/file-conventions/route
・Next.js公式ドキュメント:https://nextjs.org/docs/app/getting-started/route-handlers-and-middleware

Discussion