mcpField
S
safe-action-tanstack-query

safe-action-tanstack-query

github.com/next-safe-action開発1447 回インストール9 週間前に更新
リポジトリ

safe-action-tanstack-query について

safe-action-tanstack-query は、next-safe-action の Server Actions を TanStack Query のミューテーションに接続するアダプターです。TanStack Query を導入済みの Next.js プロジェクト向けで、結果内のサーバーエラーや検証エラーを型付きエラーに変換します。

主な機能

  1. 01TanStack Query のミューテーションオプション生成
  2. 02サーバー・検証エラーを ActionMutationError に変換
  3. 03エラー型判定ヘルパー
  4. 04リトライ、キャッシュ無効化、オフラインミューテーション対応

活用シーン

  1. 01TanStack Query プロジェクトで Server Actions を実行
  2. 02ミューテーション後にクライアントクエリキャッシュを無効化
  3. 03失敗したミューテーションのリトライ設定
  4. 04オフライン中に保留したミューテーションの永続化

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/next-safe-action/skills --skill safe-action-tanstack-query

インストール後、Claude Code で/safe-action-tanstack-queryを入力して読み込まれたか確認します。

同梱ファイルmutation-patterns.md

詳細

next-safe-action TanStack Query アダプター

インストール

npm install @next-safe-action/adapter-tanstack-query @tanstack/react-query

インポート

import {
  mutationOptions,
  ActionMutationError,
  isActionMutationError,
  hasServerError,
  hasValidationErrors,
} from "@next-safe-action/adapter-tanstack-query";

クイックスタート

"use client";

import { useMutation } from "@tanstack/react-query";
import { mutationOptions } from "@next-safe-action/adapter-tanstack-query";
import { createUser } from "@/app/actions";

export function CreateUserForm() {
  const { mutate, isPending, isError, error, data } = useMutation(
    mutationOptions(createUser, {
      onSuccess: (data) => {
        toast.success(`Created ${data.name}`);
      },
    })
  );

  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      const fd = new FormData(e.currentTarget);
      mutate({ name: fd.get("name") as string, email: fd.get("email") as string });
    }}>
      <input name="name" required />
      <input name="email" type="email" required />
      <button type="submit" disabled={isPending}>
        {isPending ? "Creating..." : "Create User"}
      </button>
      {isError && <p className="text-red-500">{error.message}</p>}
      {data && <p>Created: {data.name}</p>}
    </form>
  );
}

仕組み

mutationOptions() は、next-safe-action の結果エンベロープを TanStack Query のエラーモデルに橋渡しする、完全な UseMutationOptions オブジェクトを作成します。

  1. mutate() / mutateAsync() に渡された入力で安全なアクションを呼び出します
  2. 結果エンベロープを確認し、serverError または validationErrors が含まれているか調べます
  3. どちらかが存在する場合は ActionMutationError をスローします(クライアント側のエラークラスであり、instanceof で判定できます)
  4. 成功時は、data を TanStack Query の TData として直接返します
  5. React のレンダーフェーズ中に必ず再スローされるよう throwOnError を組み合わせて、ナビゲーションエラー(redirect()、notFound() など)を処理します

returnServerError() が生成する想定内のエラーは、他のサーバーエラーと同様に serverError に入るため、型付きペイロードを持つ ActionMutationError として通知されます。throwServerError とは異なり、mutationOptions() と完全に互換性があります。

どちらを使うべきか

シナリオ推奨
TanStack Query を使わない新しい Next.js プロジェクト組み込みフック
シンプルなフォーム送信やボタンアクション組み込みフック
React の useOptimistic による即時の楽観的 UI組み込みフック(useOptimisticAction)
キューに入れて蓄積する必要がある、重複する書き込み(並べ替え、カンバン)組み込みフック(useOptimisticStateAction)
ユーザーとは独立して変化するデータ(ソケット、ポーリング、バックグラウンド更新)読み取りには TanStack Query / SWR、ミューテーションにはアダプター
依存関係を一切追加したくない場合組み込みフック
データ取得にすでに TanStack Query を使用している場合アダプター
tRPC と TanStack Query をすでに使用している場合アダプター
バックオフ付きの自動リトライが必要な場合アダプター
ミューテーション後にクライアントのクエリキャッシュを無効化する必要がある場合アダプター
ミューテーションに TanStack Query DevTools を使いたい場合アダプター
オフラインでのミューテーション永続化が必要な場合アダプター

機能の比較

機能組み込みフックアダプター
React トランジションはい。アクションは startTransition 内で実行されますいいえ
楽観的更新React の useOptimistic を介した useOptimisticActiononMutate とクエリキャッシュを使って手動で行います
自動リトライいいえはい。バックオフ付きの retry オプション
サーバーキャッシュの無効化はい。revalidatePath() / revalidateTag()はい。同じ Next.js API
クライアントクエリキャッシュの無効化いいえ(該当しません)はい。queryClient.invalidateQueries()
DevToolsいいえはい。TanStack Query DevTools
エラーモデル結果エンベロープ(result.serverError、result.validationErrors)型ガード付きの ActionMutationError をスロー
オフラインでのミューテーション永続化いいえはい。dehydrate/hydrate による一時停止中のミューテーション
非同期実行executeAsync() は Promise<Result> を返しますmutateAsync() は Promise<Data> を返します
ステータスの追跡status 文字列と簡略形のブール値ブール値フラグ(isPending、isError、isSuccess)
追加の依存関係なし(React のみ)@tanstack/react-query

一般的な指針: ほとんどの Next.js アプリでは、組み込みフックを優先してください。追加の依存関係が不要で、React の並行レンダリングと統合されています。TanStack Query がすでに技術スタックに含まれている場合、特にキャッシュの無効化、リトライ、DevTools、オフラインサポートが必要な場合は、アダプターを優先してください。

ミューテーションのみの理由

このアダプターが提供するのは mutationOptions() のみです。設計上、queryOptions() はありません。

  • Server Actions は POST のみを使うため、GET ベースのクエリには適していません
  • Server Actions はクライアントごとにキューに入るため、リクエストのウォーターフォールが発生します
  • POST リクエストはブラウザーキャッシュ、ETag、条件付きリクエストを回避します
  • TanStack Query の重複排除に使える、安定したリソース識別子がありません

データ取得には、サーバー側では Server Components、クライアント側では Route Handlers + useQuery、またはフルスタックの型安全性が必要な場合は tRPC を使用してください。

エントリーポイント

エントリーポイントエクスポート環境
@next-safe-action/adapter-tanstack-querymutationOptions、ActionMutationError、isActionMutationError、hasServerError、hasValidationErrors、型クライアント

重要な制約

ステートレスな .action() 関数でのみ動作します。 mutationOptions() は SingleInputActionFn を受け取るため、.stateAction() をラップすることはできません。ステートフルなアクションには、next-safe-action/hooks の useStateAction / useOptimisticStateAction を使用してください。

例外をスローしないアクションでのみ動作します。 mutationOptions() に渡すアクションでは、throwValidationErrors: true や throwServerError: true を使用しないでください。このアダプターは、エラーを検出するために結果エンベロープを調べます。エラーが返されずにスローされた場合、アダプターは構造化されたエラーデータを抽出できず、型安全なエラー処理ができなくなります。TypeScript は NonThrowingActionConstraint によってこれを強制します。

関連ドキュメント

アンチパターン

// BAD: Using throwValidationErrors with adapter — errors bypass the result envelope
const client = createSafeActionClient({ throwValidationErrors: true });
const action = client.inputSchema(schema).action(async ({ parsedInput }) => { ... });
mutationOptions(action); // TypeScript error! NonThrowingActionConstraint not met

// BAD: Using mutationOptions for data fetching — server actions are POST-only
const { data } = useQuery(mutationOptions(fetchUsers)); // Wrong! Use Route Handler + useQuery

// BAD: Manually calling the action inside mutationFn
useMutation({
  mutationFn: async (input) => {
    const result = await myAction(input); // Loses error bridging, navigation handling
    return result.data;
  },
});

// GOOD: Let mutationOptions handle the bridging
useMutation(mutationOptions(myAction));

コメント