"use server"; でexportした関数が意図せず?公開される
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の仕組みを理解してコードを書いていきましょう。
Discussion
有益な情報ありがとうございます!