【问题标题】:my graphql server mutation return null value我的GraphQL Server突变返回空值
【发布时间】:2018-10-17 19:36:58
【问题描述】:

我在检索我的突变结果时遇到了挑战。我需要创建一个数据库记录并发送一封电子邮件通知用户注册成功。因为电子邮件的发送和数据库更新都是服务器端的,所以我想在同一个突变中进行。如果电子邮件消息失败,则不得更新数据库。所以我有以下突变:

Mutation: {
        createDealer(_, params) {
            console.log("params: " + JSON.stringify(params));

            bcrypt.genSalt(10, function(err, salt) {
                bcrypt.hash(params.dealer.password, salt, function(err, hash) {
                    // Store hash in your password DB.
                    console.log("hashed password " + params.dealer.password)
                    params.dealer.password = hash;
                    console.log("hashed password " + params.dealer.password + " Hash: " + hash);
                    let session = driver.session();


                    let query = "CREATE (d:Dealer {email:$dealer.email}) SET d += $dealer RETURN d";
                    let here = "here".link("mymail@example.com");

                    let messageObj = {
                        to: params.dealer.email,
                        subject: 'Dealer Registration',
                        text: `Thank you for signing up. To complete and activate your registration please click ${here}.`
                    }

                    return (sendEmail(messageObj))
                        .then(data => {
                            console.log('SendMail data....' + JSON.stringify(data));
                            return session.run(query, params)
                        })
                        .then(result => {
                            console.log('SendNeo4j data....' + JSON.stringify(result));
                            return result.records[0].get("d").properties
                        })
                        .catch((err) => {
                            console.log(err);
                        });
                    //});
                });
            }); // genSalt
        } // Create Dealer
    }, // Mutation

即使认为这两个操作都成功,我似乎也无法检索结果。我得到“未定义”: console.log('SendMail 数据....' + JSON.stringify(data)); 尽管 console.log('SendNeo4j 数据....' + JSON.stringify(result)); 确实显示正确的数据

但 graphiql 为变异返回“null”。 这是 graphiql 突变:

mutation CreateDealer($dealer: DealerInput!) {
  createDealer(dealer: $dealer) {
    email
    name
  }
}

当然是 DealerInput 变量。

我已经阅读了在哪里可以从查询/突变中检索多个结果,但我不确定它是如何工作的。在这里,我需要我的 Angular/apollo 前端的 sendEmail 和数据库更新的结果......我会成像 graphiql 对 sendEmail 一无所知,但我希望它返回我请求的属性。

发送电子邮件:

module.exports = (message) =>
    new Promise((resolve, reject) => {
        const data = {
            from: 'mymail@example.com',
            to: message.to,
            subject: message.subject,
            text: message.text
        };

        mailgun.messages().send(data, (error) => {
            if (error) {
                return reject(error);
            }
            return resolve();
        });
    });

比我有经验的人能帮我吗...谢谢

【问题讨论】:

    标签: javascript angular graphql es6-promise apollo


    【解决方案1】:

    这里有几件事要解决。在回调中返回一个 Promise(或任何其他值)不会做任何事情,这样做不会让你像你想要的那样链接额外的 Promise。相反,您的承诺会在回调中被触发并且不会被等待。

    作为一般经验法则,不要混合使用 Promise 和回调。如果您绝对必须使用回调,请始终将回调包装在 Promise 中(就像您在 sendMail 中所做的那样)。幸运的是,当今大多数流行的库都支持回调和 Promise。以下是重构上述代码以正确链接所有 Promise 的方法:

    createDealer(_, params) {
      return bcrypt.hash(params.dealer.password, 10) // note the return here!
        .then(hash => {
          params.dealer.password = hash
          const session = driver.session()
          const query = "CREATE (d:Dealer {email:$dealer.email}) SET d += $dealer RETURN d"
          const here = "here".link("mymail@example.com")
          const messageObj = {
             to: params.dealer.email,
             subject: 'Dealer Registration',
             text: `Thank you for signing up. To complete and activate your registration please click ${here}.`
          }
          return sendEmail(messageObj) // note the return here!
        }).then(data => {
          return session.run(query, params) // note the return here!
        }).then(result => {
          result.records[0].get("d").properties // note the return here!
        })
    
    • bcrypt.hash 会自动为你生成盐,如果你不传入一个 - 不需要调用两个单独的函数
    • 我们用bcrypt.hash 启动我们的Promise 链,所以我们需要返回它返回的Promise。解析器必须返回一个值或将解析为一个值的 Promise,否则返回 null。
    • 在每个then 中,我们返回一个Promise。通过这种方式,我们“链接”了我们的 Promise,让我们在解析器中返回的最终值成为链中最后一个 Promise 解析为的值。

    我们还需要修复您的 sendMail 函数以实际返回值。您在函数内正确返回了新的 Promise,但您还需要传递返回的 data 对象来解析。这告诉 Promise 解析为该值。

    module.exports = (message) => new Promise((resolve, reject) => {
      const data = // ...etc
      mailgun.messages().send(data, (error) => {
        if (error) reject(error) // no need to return, it's pointless
        resolve(data) // pass data to resolve
      })
    })
    

    旁注:看起来official mailgun library 支持 Promises。

    此外,我强烈建议您考虑使用 async/await,尤其是在处理长 Promise 链时。它更不容易出错且更易读:

    createDealer async (_, params) {
      const hash = await bcrypt.hash(params.dealer.password)
      params.dealer.password = hash
      const session = driver.session()
      const query = "CREATE (d:Dealer {email:$dealer.email}) SET d += $dealer RETURN d"
      const here = "here".link("mymail@example.com")
      const messageObj = {
        to: params.dealer.email,
        subject: 'Dealer Registration',
        text: `Thank you for signing up. To complete and activate your registration please click ${here}.`
      }
      const emailResult = await sendEmail(messageObj)
      const result = await session.run(query, params)
      return result.records[0].get("d").properties // still need to return!
    }
    

    编辑:关于捕获错误,GraphQL 将捕获解析器抛出的任何错误,这意味着您通常可以自己跳过使用catch。例如,如果您的 mailgun 请求失败,它将生成某种错误,并且您的查询将为 dataerrors 数组中的错误详细信息返回 null。

    这可能就足够了,尽管 1) 你可能想在别处记录你的错误堆栈; 2) 在生产环境中,您可能不想将内部错误详细信息公开。

    这意味着您可能想要使用自定义错误。作为奖励,您可以在错误中添加一些自定义属性,以帮助客户雄辩地处理它们。所以你的代码最终可能看起来更像这样:

    class DeliveryFailureError extends Error {}
    DeliveryFailureError.code = 'DELIVERY_FAILURE'
    DeliveryFailureError.message = 'Sorry, we could not deliver the email to your account'
    
    try {
      await mailgun.messages.create()
    } catch (err) {
      logger.error('Mailgun request failed:', err.stack)
      throw new DeliveryFailureError()
    }
    

    【讨论】:

    • 我确实更喜欢 async/await 优雅,但我也只是倾向于使用它。一个问题.....我如何获得从sendEmail返回的值?如果 sendEmail 失败,我需要在前端知道这一点。
    • 你能解释一下 catch 错误是如何处理的吗?我知道如果 sendMail 失败,那么错误将被 .catch 捕获,并且不会尝试创建数据库记录……graphql 如何处理此错误?它只是返回一个空值吗?我怎么知道它的 sendEmail 与数据库更新相比失败了?
    • 显然,如果没有盐长度,bcrypt salt auto-gen 将无法工作..它会引发错误。除此之外,我的 graphiql 现在可以工作了
    • 查看我的编辑以获取有关捕获错误的更详细说明
    • 感谢您的帮助我会花费比我更多的时间来解决这个问题。对于查看此答案的任何人,请注意声明的范围需要进行一些调整。会话和查询需要在 return session.run 块中
    猜你喜欢
    • 2021-04-25
    • 2021-04-01
    • 2021-06-05
    • 2019-03-30
    • 2021-02-27
    • 2018-12-19
    • 2019-06-28
    • 2020-12-22
    • 2020-02-25
    相关资源
    最近更新 更多