mcpField
S
safe-action-client

safe-action-client

github.com/next-safe-action开发11.2k 次安装8 周前更新
代码仓库

关于 safe-action-client

safe-action-client 是用于在 TypeScript 中定义和调用 next-safe-action 服务端 action 的技能,涵盖链式客户端 API、模式验证、中间件和客户端 Hooks。

主要功能

  1. 01配置链式 action 客户端
  2. 02使用模式验证输入与输出
  3. 03添加验证前后中间件
  4. 04定义元数据和绑定参数模式
  5. 05使用 action 与有状态客户端 Hooks

适用场景

  1. 01设置类型安全的服务端 action
  2. 02验证 action 输入和返回值
  3. 03通过 Hooks 处理客户端 action 状态

安装

复制下面的命令,在终端中运行。

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

安装后在 Claude Code 中输入/safe-action-client确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

client-setup.md
error-handling.md
input-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)——它会将 result.serverError 设置为带类型的负载,并绕过 handleServerError。请参阅服务端错误处理。

服务端代码函数参数

传递给 .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(),会添加第二个参数:

.stateAction(async ({ parsedInput, ctx }, { prevResult }) => {
  // prevResult is the previous SafeActionResult (structuredClone'd)
  return { count: (prevResult.data?.count ?? 0) + 1 };
});

评论

支持 Markdown