Skip to content

Instantly share code, notes, and snippets.

@kobitoDevelopment
Created April 22, 2026 07:21
Show Gist options
  • Select an option

  • Save kobitoDevelopment/1cbcfaef2d9f6562a523aadbdcbd3114 to your computer and use it in GitHub Desktop.

Select an option

Save kobitoDevelopment/1cbcfaef2d9f6562a523aadbdcbd3114 to your computer and use it in GitHub Desktop.

"use server" に関する備忘録

基本動作

  • "use server" を付けた関数(Server Action)は、実装コードはサーバー側にのみ存在し、クライアントバンドルには含まれない。
  • Next.js は自動でエンドポイントを生成し、クライアントから POST リクエストで呼び出せる状態になる。
  • 結果として、認証されていないユーザーを含め、誰でもそのエンドポイントを呼び出せる。

セキュリティ上の扱い

  • "use server" 関数は公開 API と同等に扱う。
  • 関数内で認証・認可・入力バリデーションを実装する必要がある。
  • closure 経由で渡した変数は暗号化されてクライアントに送られるため、秘密情報を closure で渡すのは避ける。

使用判断

"use server" を付ける場面

  • クライアント(フォーム送信、ボタンクリック等)から直接呼び出す関数。
  • 例: フォーム送信、いいねボタン、コメント投稿。

"use server" を付けない場面

  • Server Component や他のサーバーコードからのみ呼び出す内部関数。
  • 例: DB アクセスのヘルパー、内部的なデータ取得処理、ユーティリティ関数。

コード例

認証なしの Server Action(危険)

"use server";

export async function deletePost(id: string) {
  await db.post.delete({ where: { id } });
}

認証・認可を実装した Server Action

"use server";

export async function deletePost(id: string) {
  const session = await auth();
  if (!session?.user) throw new Error("Unauthorized");

  const post = await db.post.findUnique({ where: { id } });
  if (post?.authorId !== session.user.id) throw new Error("Forbidden");

  await db.post.delete({ where: { id } });
}

内部関数として定義する場合

// lib/users.ts
export async function getAllUsersWithPasswords() {
  return await db.user.findMany();
}
// page.tsx(Server Component)
import { getAllUsersWithPasswords } from "@/lib/users";

export default async function Page() {
  const users = await getAllUsersWithPasswords();
  return <div>...</div>;
}

判断基準

  • クライアントから呼び出す必要があるか。
    • Yes → "use server" を付ける。
    • No → 付けない。
  • "use server" を付けた時点で公開エンドポイントが生成される。
  • Server Component は "use server" なしでサーバー上で実行されるため、内部ロジックに付ける必要はない。

onClick などのイベントハンドラで呼び出す関数

  • "use server" の要否は、呼び出し元が onClick かどうかではなく、関数の実行場所で決まる。
  • クライアントで完結する処理(state 更新、DOM 操作、公開 API への fetch 等): "use server" 不要。
  • サーバーリソース(DB、ファイルシステム、秘密鍵等)にアクセスする処理: "use server" 必要。

クライアントで完結する例

"use client";

export function Counter() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count + 1);
  };

  return <button onClick={handleClick}>{count}</button>;
}

サーバー処理を呼び出す例

// actions.ts
"use server";
export async function likePost(id: string) {
  await db.post.update({ where: { id }, data: { likes: { increment: 1 } } });
}
// LikeButton.tsx
"use client";
import { likePost } from "./actions";

export function LikeButton({ id }: { id: string }) {
  return <button onClick={() => likePost(id)}>Like</button>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment