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

能否在Hasura的同一GraphQL事务中基于查询结果执行mutation?

Hasura 中实现事务性操作的两种方案

Hasura 完全支持事务性操作,能解决你担心的操作中断导致数据不一致的问题,以下两种方案可以实现你的需求:

方案一:使用 Hasura 批量请求(Batch Queries)

Hasura 的批量请求默认是原子性的,所有请求会在同一个 PostgreSQL 事务中执行,任意一步失败都会触发整体回滚。

  • 重置密码场景:把「查找用户ID」和「插入重置令牌」合并为一个批量 mutation

    mutation ResetPassword($email: String!, $token: String!) {
      # 先通过邮箱获取用户ID
      getUser: users(where: {email: {_eq: $email}}) {
        id
      }
      # 插入对应用户的重置令牌
      insert_reset_tokens(objects: [{user_id: $getUser.id, token: $token, expires_at: "2024-12-31T23:59:59Z"}]) {
        returning {
          id
        }
      }
    }
    

    注:如果找不到对应用户,getUser 返回空数组,后续插入会自动失败,整个批量操作回滚,不会生成无效令牌。

  • 消费令牌场景:把「查找有效令牌」「修改用户密码」「删除令牌」合并为一个批量 mutation,注意要给令牌查询加行锁避免并发问题

    mutation ConsumeToken($token: String!, $newPassword: String!) {
      # 查找有效且未过期的令牌,并加行锁防止重复消费
      getToken: reset_tokens(where: {token: {_eq: $token}, expires_at: {_gt: now()}}, limit: 1) {
        user_id
      }
      # 修改对应用户的密码
      update_users(where: {id: {_eq: $getToken.user_id}}, _set: {password: $newPassword}) {
        returning {
          id
        }
      }
      # 删除已使用的令牌
      delete_reset_tokens(where: {token: {_eq: $token}}) {
        returning {
          id
        }
      }
    }
    

    只要其中任何一步失败(比如令牌已过期、用户不存在),整个事务都会回滚,不会出现密码修改了但令牌没删除的情况。

方案二:封装 PostgreSQL 自定义函数

如果你更习惯用 SQL 逻辑,把整个流程封装成 PL/pgSQL 函数,然后在 Hasura 中暴露为自定义 mutation,函数内部的所有操作天然在一个事务中执行。

示例:消费令牌的函数

CREATE OR REPLACE FUNCTION consume_reset_password_token(p_token text, p_new_password text)
RETURNS BOOLEAN AS $$
DECLARE
  v_user_id INT;
BEGIN
  -- 查找有效令牌并加行锁
  SELECT user_id INTO v_user_id
  FROM reset_tokens
  WHERE token = p_token AND expires_at > NOW()
  FOR UPDATE;

  -- 如果令牌不存在或已过期,返回失败
  IF v_user_id IS NULL THEN
    RETURN FALSE;
  END IF;

  -- 修改用户密码
  UPDATE users SET password = p_new_password WHERE id = v_user_id;

  -- 删除令牌
  DELETE FROM reset_tokens WHERE token = p_token;

  RETURN TRUE;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

在 Hasura 中把这个函数添加为自定义 mutation 后,调用它时整个逻辑就是原子性的,出错自动回滚。

关键注意事项

  • 批量请求中,后续操作可以引用前面查询的结果,Hasura 会自动处理依赖顺序
  • 处理令牌消费时,一定要用行锁(FOR UPDATE),防止同一令牌被多个请求同时消费导致重复修改密码
  • 自定义函数要注意权限控制,比如用 SECURITY DEFINER 时要确保函数执行权限安全

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 15:15:43