Zenn
🫣

"use server"; でexportした関数が意図せず?公開される

2024/07/08に公開4
1

Next.js AppRouterで利用できるReactのServer Actions機能。クライアントからサーバ上の処理を関数で呼び出せるので非常に便利ですが、 "use server"; のことをあまり知らず、誤った使い方をすると意図せず公開したくない関数が外部に公開されてしまうケースがあるので注意です(ほとんどこんなケースはないと思いますが、なくはないので注意喚起です)。

Server Actionsの例

Server Actions用の関数として宣言するためには "use server"; が必要です。それ以外は至って普通の非同期関数で大丈夫です。

"use server";

export async function someAction() {
  return {
    message: "Server Action",
  };
}

次に定義したServer Actionsの関数を呼び出すクライアントコンポーネントの例です。ボタンをクリックすると、上記の関数をサーバ上で実行し {message: "someAction"} というレスポンスを返します。

"use client";

import { someAction } from "@/app/someAction";

export function CallButton() {
  return (
    <button
      type="button"
      onClick={async () => {
        const result = await someAction();
        console.log(result);
      }}
    >
      Call
    </button>
  );
}

実装コードは非常にシンプルですね。

Server Actionsはどう実現されているのか

クライアントからサーバ上の処理を関数で呼び出せる…というのは、実態としては fetch 関数でPOSTリクエストをしているだけです。具体的なリクエストをcurlで再現すると以下のようになっています。

curl 'http://localhost:3000/' \
  -X POST \
  -H 'Content-Type: text/plain;charset=UTF-8' \
  -H 'Next-Action: c067dbb1c3ea2bce4b8204d09befc20f9b40ace4' \
  --data-raw '[]'

ポイントはリクエストヘッダの Next-Action です。この値は、Server Actionsの関数を特定するためのIDです。

また、レスポンスは定義した関数の返り値が専用のフォーマットで返ってきます。

0:["$@1",["development",null]]
1:{"message":"Server Action"}

つまりServer Actionsとは言ってしまえば単なる普通の公開APIです。

公開するつもりのない関数がさらされるケース

こういうコードを書くことはほとんどないと思うのですが、度重なるコード修正をしていると混ざってしまうかもしれません。以下のようなServer Actionsは危険です。

"use server";

export async function someAction() {
  const result = await privateFunc();
  return {
    message: result,
  };
}

// プライベート用途の関数だが…
export async function privateFunc() {
  return "private function";
}

privateFunc() 関数の定義を見ると export されてしまっています。これは別のファイルで再利用したいという意図かもしれないし、ケアレスミスかもしれないですが、非常に危険です。

なぜなら "use server"; が付いたファイルで export された関数はすべて外部から呼び出すことが出来るEndpointとして生成されてしまうから です。これはつまり privateFunc() を誰でも呼び出すこと出来るということです。

最初に紹介したcurlの Next-Action ヘッダで利用するIDさえ分かってしまえばリクエストができてしまいます。そしてそのIDは普通にブラウザ側のJavaScriptファイルに出力されているため簡単に判明します。


スクリプトにServer ActionsのIDが記載されいてる様子

まとめ

"use server"; が付いたファイルで export するということは、外部から呼び出せるということ。Server Actionsの仕組みを理解してコードを書いていきましょう。

1
ムーザルちゃんねる

Discussion

ryoppippiryoppippi

こんにちは!
質問があります!
例えばプライベート関数でもテストを書く際にはテスト用のファイルから実装を呼び出すためには、関数にexportをつける必要があると思います。
しかし実際にデプロイされるときにはそのままでは exportがついているので外部に露出してしまうのですね。
外部に露出させたくはないが、別ファイルでテストを書きたいときはどのようにすればいいでしょう。
(vitestのin-source testingは一つの解答になりうるのですが、やはりテストの実装はファイルを分けることが多いと思うのでこの質問をさせていただきました)

private functionは 'use server' のついていない別のファイルに記載するのが正解でしょうか。

zaruzaru

質問ありがとうございます。結論から言うと export したいなら別ファイルに切り出したほうが安全です。

Next.js v15からはソースコード上にServer Actions関数を特定するIDが不用意に露出しないようになっているので、おおきく問題にはならないかもしれませんが外部からリクエストされうる状況は変わりません。つまり、外部から呼び出されると困るような関数はおっしゃるように "use server"; のないファイルに分離するのが安全です。

語弊がある言い方かもしれませんが "use server"; は、いわゆるWeb MVCフレームワークで言うコントローラ(ルーティング機能付き)に該当し、外部からのリクエストを受け取って、何らかのレスポンスを返す機能です。詳細なビジネスロジックは別ファイル(MVCのM)に切り出す…みたいなイメージだと切り分けしやすいかもしれませんね。

ryoppippiryoppippi

了解です、ありがとうございます。
"use server"を使ってexportした時点でもうそれはAPI Endpoint」これだけ覚えて帰ろうと思います!

ログインするとコメントできます