safe-action-client について
safe-action-clientは、TypeScriptでnext-safe-actionのサーバーアクションを定義・利用するためのスキルです。チェーン式API、スキーマ検証、ミドルウェア、クライアントフックを扱います。
主な機能
- 01チェーン式アクションクライアントの設定
- 02スキーマによる入出力の検証
- 03検証前後のミドルウェア追加
- 04メタデータとバインド引数のスキーマ定義
- 05アクション用・ステートフルなクライアントフックの利用
活用シーン
- 01型付きサーバーアクションの設定
- 02アクションの入力と戻り値の検証
- 03フックによるクライアント側状態の管理
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ next-safe-action/ skills --skill safe-action-clientインストール後、Claude Code で/safe-action-clientを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
client-setup.mderror-handling.mdinput-output-validation.md詳細
next-safe-action クライアントとアクションの定義
クイックスタート
// src/lib/safe-action.ts
import { createSafeActionClient } from "next-safe-action";
export const actionClient = createSafeActionClient();
// src/app/actions.ts
"use server";
import { z } from "zod";
import { actionClient } from "@/lib/safe-action";
export const greetUser = actionClient
.inputSchema(z.object({ name: z.string().min(1) }))
.action(async ({ parsedInput: { name } }) => {
return { greeting: `Hello, ${name}!` };
});
チェーン可能な API の順序
createSafeActionClient(opts?)
.use(middleware) // repeatable, adds pre-validation middleware
.metadata(data) // required if defineMetadataSchema is set
.inputSchema(schema, utils?) // Standard Schema or async factory function
.bindArgsSchemas([...]) // schemas for .bind() arguments (order with inputSchema is flexible)
.useValidated(middleware) // repeatable, adds post-validation middleware (requires inputSchema or bindArgsSchemas before it)
.outputSchema(schema) // validates action return value
.action(serverCodeFn, utils?) // creates SafeActionFn
.stateAction(serverCodeFn, utils?) // creates SafeStateActionFn (for useStateAction, useOptimisticStateAction, or React's useActionState)
各メソッドは新しいクライアントインスタンスを返します。チェーンは不変です。
エントリーポイント
| エントリーポイント | 環境 | エクスポート |
|---|---|---|
next-safe-action | サーバー | createSafeActionClient、createMiddleware、createValidatedMiddleware、returnValidationErrors、returnServerError、flattenValidationErrors、formatValidationErrors、DEFAULT_SERVER_ERROR_MESSAGE、エラークラス、すべてのコア型 |
next-safe-action/hooks | クライアント | useAction、useOptimisticAction、useStateAction、useOptimisticStateAction、フックの型 |
next-safe-action/stateful-hooks | クライアント | useStateAction(後方互換性のために hooks から再エクスポート) |
関連ドキュメント
アンチパターン
// BAD: Missing "use server" directive — action won't work
import { actionClient } from "@/lib/safe-action";
export const myAction = actionClient.action(async () => {});
// GOOD: Always include "use server" in action files
"use server";
import { actionClient } from "@/lib/safe-action";
export const myAction = actionClient.action(async () => {});
// BAD: Calling .action() without .metadata() when metadataSchema is defined
const client = createSafeActionClient({
defineMetadataSchema: () => z.object({ actionName: z.string() }),
});
client.action(async () => {}); // TypeScript error!
// GOOD: Always provide metadata before .action() when schema is defined
client
.metadata({ actionName: "myAction" })
.action(async () => {});
// BAD: Returning an error instead of throwing
export const myAction = actionClient
.inputSchema(z.object({ email: z.string().email() }))
.action(async ({ parsedInput }) => {
const exists = await db.user.findByEmail(parsedInput.email);
if (exists) {
return { error: "Email taken" }; // Not type-safe, not standardized
}
});
// GOOD: Use returnValidationErrors for field-level errors
import { returnValidationErrors } from "next-safe-action";
export const myAction = actionClient
.inputSchema(z.object({ email: z.string().email() }))
.action(async ({ parsedInput }) => {
const exists = await db.user.findByEmail(parsedInput.email);
if (exists) {
returnValidationErrors(z.object({ email: z.string().email() }), {
email: { _errors: ["Email is already in use"] },
});
}
return { success: true };
});
想定される検証以外のビジネスエラー(「在庫切れ」「見つかりません」など)には、代わりに returnServerError(payload) を使用してください。これにより、handleServerError を経由せず、result.serverError に型付きペイロードが設定されます。詳しくはサーバーエラーの処理を参照してください。
サーバーコード関数のパラメーター
.action() に渡される関数は、単一のオブジェクトを受け取ります。
.action(async ({
parsedInput, // validated input (typed from inputSchema)
clientInput, // raw client input (unknown)
bindArgsParsedInputs, // validated bind args tuple
bindArgsClientInputs, // raw bind args
ctx, // context from middleware chain
metadata, // metadata set via .metadata()
}) => {
// return data
});
.stateAction() の場合、第 2 引数が追加されます。
.stateAction(async ({ parsedInput, ctx }, { prevResult }) => {
// prevResult is the previous SafeActionResult (structuredClone'd)
return { count: (prevResult.data?.count ?? 0) + 1 };
});
コメント
Markdown 対応