You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Server Actions+TanStack Query更新字段时遇PrismaClient浏览器错误

问题:Server Actions + TanStack Query 更新数据库时触发 PrismaClient 浏览器环境错误

Error: PrismaClient is unable to run in this browser environment, or has been bundled for the browser (running in unknown). If this is unexpected, please open an issue:
at Object.get (index-browser.js:203:15)
at getDataById (get-all-apointment.ts:55:27)
at approvedStatus (update-status.ts:11:45)
at Object.mutationFn (admin-column.tsx:140:62)
at Object.fn (mutation.js:72:29)
at run (retryer.js:92:31)
at Object.start (retryer.js:133:9)
at Mutation.execute (mutation.js:107:40)

我尝试用 Server Actions 结合 TanStack Query 更新数据库单个字段,但点击“批准按钮”时触发上述错误。相关代码如下:

列定义代码

export type Events = {
  id: string;
  title: string;
  email: string;
  fullName: string;
  contactPerson: string;
  department: string;
  status: string;

  dateOfEvent: string;
  startingTime: string;
  endingTime: string;

  purpose: string;

  doesHaveDryRun: boolean;
  dryRunDate?: string | null; // 可选
  dryRunStart?: string | null; // 可选
  dryRunEnd?: string | null; // 可选

  doesHaveTCETAssitance: string;
  tcetOtherAssitance?: string | null; // 可选

  meetingTypeOption: string;
  meetingTypeServices: string;
  meetingTypeServiceLink?: string | null; // 可选
  cameraSetup?: string | null; // 可选
};

export const columns: ColumnDef<Events>[] = [
/* 其他列 */
  {
    id: "actions",

    cell: ({ row }) => {
      const data = row.original;
      const router = useRouter(); // 获取路由实例

      const { mutate: approvedButton } = useMutation({
        mutationFn: async (id:string) => await approvedStatus(id),
        onError: (error) => {
          console.log(error);
        },
        onSuccess: () => {
          router.push("/admin/calendar");
          router.refresh();
        },
      });


      return (
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button variant="ghost" className="h-8 w-8 p-0">
              <span className="sr-only">打开菜单</span>
              <MoreHorizontal className="h-4 w-4" />
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            <DropdownMenuLabel>操作</DropdownMenuLabel>

            <DropdownMenuItem
              onClick={() => router.push(`/admin/edit/${data.id}`)}
            >
              <PanelRightCloseIcon className="mr-2 h-4 w-4" />
              查看详情
            </DropdownMenuItem>

            <DropdownMenuSub>
              <DropdownMenuSubTrigger>
                <UserPlus className="mr-2 h-4 w-4" />
                <span>更新状态</span>
              </DropdownMenuSubTrigger>
              <DropdownMenuPortal>
                <DropdownMenuSubContent>
                <DropdownMenuItem onClick={() => approvedButton(data.id)}>
                    <Check className="mr-2 h-4 w-4 text-emerald-600" />
                    <span>已批准</span>
                  </DropdownMenuItem>
                  <DropdownMenuItem>
                    <CircleDot className="mr-2 h-4 w-4 text-rose-600" />
                    <span>待处理</span>
                  </DropdownMenuItem>
                  <DropdownMenuSeparator />
                  
                </DropdownMenuSubContent>
              </DropdownMenuPortal>
            </DropdownMenuSub>
            <DropdownMenuSeparator />
            <DropdownMenuItem>
              <Trash2 className="mr-2 h-4 w-4" />
              删除
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      );
    },
  },
];

Server Actions 代码

export const approvedStatus = async (id:string) => {
    try {
        const isExisting = await getDataById(id)
        if(!isExisting) return {error: "数据不存在"}

        await db.appoinmentSchedule.update({
            where: {
                id: id
            },
            data: {
                status: 'approved'
            }
        })

        return {success: "事件状态更新成功!"}
    } catch (error) {
        console.log(error)
        throw error
   
    }
}

解决方案

这个错误的核心原因是你的 Server Action 被打包到浏览器端执行了,而 PrismaClient 只能在 Node.js 服务器环境运行,无法在浏览器中调用。

1. 标记 Server Action

在 Next.js 中,必须用 'use server' 指令明确标记 Server Action,确保它只在服务器端执行。修改你的 approvedStatus 函数:

'use server' // 必须放在文件最顶部

export const approvedStatus = async (id:string) => {
    try {
        const isExisting = await getDataById(id)
        if(!isExisting) return {error: "数据不存在"}

        await db.appoinmentSchedule.update({
            where: {
                id: id
            },
            data: {
                status: 'approved'
            }
        })

        return {success: "事件状态更新成功!"}
    } catch (error) {
        console.log(error)
        throw error
   
    }
}

2. 检查关联函数

getDataById 函数如果也使用了 PrismaClient,同样需要标记 'use server',确保它也是服务器端执行的逻辑,不会被客户端打包。

3. 确认代码目录规范

建议将 Server Action 放在 app/actions/ 目录下,Next.js 会自动识别该目录下的文件为服务器端代码,避免被客户端打包。

4. 保持客户端调用逻辑正确

你当前用 TanStack Query 的 mutationFn 调用 Server Action 的写法是正确的,只要确保 approvedStatus 被正确标记为服务器端函数,Next.js 会自动处理客户端到服务器的请求转发。

内容的提问来源于stack exchange,提问作者Stykgwar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 11:05:23