🍪

【App Router】Next.jsのRoute HandlersとServer Actions、どう使い分ける?

2024/07/12に公開

はじめに

こんにちは。ココナラテックの開発をしているエンジニアのもちさんです。

ココナラではエージェント事業部で使っているフロントエンドのフレームワークとして Next.js を採用しており、2023 年の 9 月から App Router への移行を始めています。

実際に App Router で Cookie を設定しなければいけない時があったのですが、Next.js の公式ドキュメントにあるように Cookie を設定するにはServer ActionRoute Handlerを使う必要がありました。

Good to know: HTTP does not allow setting cookies after streaming starts, so you must use .set() in a Server Action or Route Handler.

出典: Functions: cookies | Next.js

App Router ではクライアント側で呼び出せるサーバーサイドのコードとして、主にRoute HandlerServer Actionが利用できます。
この記事では、両者の違いを自分の分かる範囲で説明していきたいと思います。

Route Handlers と Server Actions の比較

前提条件

ここでは例として、Route Handlers と Server Actions で Cookie を設定する方法を説明します。
プロジェクトは下記コマンドで作成したもの(執筆時点では Next.js 14)を利用します。

npx create-next-app@latest --typescript --app --eslint --src-dir

Route Handlers で実装する場合

src/app/api/set-cookie/route.ts
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

const POST = async (): Promise<NextResponse> => {
  await cookies().set("foo", "bar");

  return NextResponse.json(
    {},
    {
      status: 200,
    }
  );
};

export { POST };
src/app/route-handler/page.tsx
"use client";

import { useEffect } from "react";

const SetCookiePage = (): JSX.Element => {
  useEffect(() => {
    const setCookie = async (): Promise<void> => {
      await fetch(`/api/set-cookie`, {
        method: "POST",
      });
    };
    setCookie();
  }, []);

  return <></>;
};

export default SetCookiePage;

Server Actions で実装する場合

src/app/actions/set-cookie.ts
"use server";

import { cookies } from "next/headers";

export async function setCookieAction(): Promise<void> {
  await cookies().set("foo", "bar");
}
src/app/server-actions/page.tsx
"use client";

import { useEffect } from "react";

import { setCookieAction } from "@/app/actions/set-cookie";

const SetCookiePage = (): JSX.Element => {
  useEffect(() => {
    const setCookie = async (): Promise<void> => {
      await setCookieAction();
    };
    setCookie();
  }, []);

  return <></>;
};

export default SetCookiePage;

備考

コードの記法は微妙に違いますが「クライアントコンポーネントから別のリクエストを呼び出し、Cookie を設定する」という点ではやっていることは同じです。

Server ActionsでのCookieセット

Route HandlersでのCookieセット

結局 Route Handlers と Server Actions どちらを使えば良かったのか?

Next.js 13 を使っている場合

Server Actions が安定版になったのは、Next.js 14 からなので Route Handlers を使うしかありません。
今回は Next.js を 14 にバージョンアップする検証の時間がないため Route Handlers を選択しましたが、
これは暫定対応であり、Next.js 14 への移行準備を進めることが前提です。

Next.js 14 以降を使っている場合

App Router で書き込みや mutations を実行する慣用的な方法は、Server Actions を使う旨が、Next.js の公式ブログに記載されています。
代表的な例として Cookie の操作が挙げられているので、Next.js 14 以降で Cookie を操作する際は大人しく Server Actions を使ったほうがいいと思われます。

The idiomatic way to perform writes or mutations in Next.js App Router is using Server Actions.

出典: How to Think About Security in Next.js | Next.js

Route Handlers と Server Actions、どう使い分ければいい?

Route Handlers と Server Actions はクライアントからアクセスできる URL が発行され、サーバー上で実行されるという点では近い存在ですが、Server Actions の一番の特色は サーバー上で実行するコードを非同期関数のように扱えることです。

例えば、以下のコードのように非同期関数のように Next.js で扱うことができます。

export default function Page() {
  async function createInvoice(formData: FormData) {
    "use server";

    const rawFormData = {
      customerId: formData.get("customerId"),
      amount: formData.get("amount"),
      status: formData.get("status"),
    };

    // mutate data
    // revalidate cache
  }

  return <form action={createInvoice}>...</form>;
}

出典: Data Fetching: Server Actions and Mutations | Next.js

なので、サーバー上で動作するコードをNext.js 内からアクセスできるようにする前提の場合は、Server Actions を使う方がいいでしょう。


たとえば、次の記述にあるように、Next.js 14 の Server Actions ではクライアントからリクエストがあった際に、Originヘッダーと Hostヘッダー(または X-Forwarded-Host)を比較します。 両者が一致しない場合、エラーが発生します。事実上、サーバーアクションは同じホスト上でしか呼び出しができません。
※ 他にもNext-Actionヘッダーの内容を検証しているなど細かい内部挙動はあるのですが、長くなるのでここでは割愛します。

As an additional protection Server Actions in Next.js 14 also compares the Origin header to the Host header (or X-Forwarded-Host). If they don't match, the Action will be rejected. In other words, Server Actions can only be invoked on the same host as the page that hosts it. Very old unsupported and outdated browsers that don't support the Origin header could be at risk.

出典: How to Think About Security in Next.js | Next.js

つまり、Server Actions では最低限の CSRF 対策は行われている状態であり、Route Handlers を使うと自前でのセキュリティ対策の実装が別途必要な状態になります。

一方で、他のアプリから API を呼び出せるようにする際には一般的に Route Handlers を使うのがいいでしょう。

最後に

エージェント開発チームは、フロントエンドやバックエンドを横断的に実装できたり、新技術に挑戦できる魅力的な環境です。
最近では、AI 関連の技術にも挑戦していたりします。小さい開発チームですが、その分、小回りが効く楽しいチームです。

興味を持って頂けた方は、是非とも採用情報に目を通してみてください!!

https://coconala.co.jp/recruit/engineer/

Discussion