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

GraphQL突变后自定义错误消息实现问题求助

解决GraphQL订阅Mailchimp时自定义错误消息的类型匹配问题

我来帮你搞定这个问题!你现在遇到的核心矛盾是:你的GraphQL突变定义了非空的NewsletterSubscription!返回类型,但捕获到"Member exists"错误时直接返回了字符串,这和GraphQL预期的对象类型完全不兼容,才导致了那个"Cannot return null for non-nullable field"的报错。

下面给你两种实用的解决方案,你可以根据需求选择:


方案1:用GraphQL自定义错误抛出(简单直接,无需改Schema)

如果你想保持原有的Schema定义不变,最简单的方式是在catch块里抛出一个GraphQLError,而不是返回字符串。GraphQL会自动把自定义错误信息放进响应的errors数组里,同时因为执行失败,data为null是符合规则的(毕竟非空类型是指成功时必须返回对象,失败时抛出错误是允许的)。

修改你的突变代码:

// 先确保导入GraphQLError
const { GraphQLError } = require('graphql');

async createNewsletterSubscription(parent, args, { db }, info) {
  const { name, email } = args.data;
  const newEmailSubscription = { email, name };
  const FNAME = name;
  
  try {
    const result = await request
      .post(
        `https://${mailchimpInstance}.api.mailchimp.com/3.0/lists/${MAILCHIMP_LIST_ID}/members`
      )
      .set(
        'Authorization',
        `Basic ${new Buffer(`any:${MAILCHIMP_APIKEY}`).toString('base64')}`
      )
      .send({
        email_address: email,
        status: 'subscribed',
        merge_fields: {
          FNAME,
        },
      });

    if (result.status === 200 && result.body.status === 'subscribed') {
      return newEmailSubscription;
    }
    
    // 处理其他非成功的情况
    throw new GraphQLError('订阅失败:未知原因');
  } catch (error) {
    const errorTitle = JSON.parse(error.response.text).title;
    
    if (errorTitle === 'Member exists') {
      // 抛出自定义友好错误
      throw new GraphQLError('这个邮箱已经订阅过我们的新闻邮件啦!');
    }
    
    // 处理其他Mailchimp错误
    throw new GraphQLError(`订阅失败:${errorTitle}`);
  }
},

修改后,当邮箱已存在时,GraphQL Playground会返回这样的响应:

{
  "data": null,
  "errors": [
    {
      "message": "这个邮箱已经订阅过我们的新闻邮件啦!",
      "locations": [
        {
          "line": 3,
          "column": 5
        }
      ],
      "path": [
        "createNewsletterSubscription"
      ]
    }
  ]
}

方案2:定义联合类型返回成功/错误结果(更友好的客户端体验)

如果你希望客户端能直接从data里拿到结果(不用依赖errors数组),可以修改Schema,定义一个联合类型,让突变既可以返回成功的订阅对象,也可以返回错误信息。

第一步:更新Schema定义

# 原有的订阅成功类型保留
type NewsletterSubscription {
  email: String!
  name: String!
}

# 新增错误类型
type SubscriptionError {
  message: String!
}

# 定义联合类型,包含成功和错误两种情况
union NewsletterSubscriptionResult = NewsletterSubscription | SubscriptionError

# 修改突变的返回类型为联合类型
type Mutation {
  createNewsletterSubscription(data: CreateNewsletterSubscriptionInput): NewsletterSubscriptionResult!
}

第二步:修改突变代码

在成功时返回订阅对象,错误时返回错误对象,注意要加上__typename字段,方便GraphQL解析联合类型:

async createNewsletterSubscription(parent, args, { db }, info) {
  const { name, email } = args.data;
  const newEmailSubscription = { email, name };
  const FNAME = name;
  
  try {
    const result = await request
      .post(
        `https://${mailchimpInstance}.api.mailchimp.com/3.0/lists/${MAILCHIMP_LIST_ID}/members`
      )
      .set(
        'Authorization',
        `Basic ${new Buffer(`any:${MAILCHIMP_APIKEY}`).toString('base64')}`
      )
      .send({
        email_address: email,
        status: 'subscribed',
        merge_fields: {
          FNAME,
        },
      });

    if (result.status === 200 && result.body.status === 'subscribed') {
      // 返回成功类型,指定__typename
      return { ...newEmailSubscription, __typename: 'NewsletterSubscription' };
    }
    
    return { 
      message: '订阅失败:未知原因', 
      __typename: 'SubscriptionError' 
    };
  } catch (error) {
    const errorTitle = JSON.parse(error.response.text).title;
    
    if (errorTitle === 'Member exists') {
      return { 
        message: '这个邮箱已经订阅过我们的新闻邮件啦!', 
        __typename: 'SubscriptionError' 
      };
    }
    
    return { 
      message: `订阅失败:${errorTitle}`, 
      __typename: 'SubscriptionError' 
    };
  }
},

第三步:客户端查询示例

客户端需要用**片段(Fragment)**来处理联合类型的不同情况:

mutation Subscribe($data: CreateNewsletterSubscriptionInput!) {
  createNewsletterSubscription(data: $data) {
    ... on NewsletterSubscription {
      email
      name
    }
    ... on SubscriptionError {
      message
    }
  }
}

此时的响应会变成这样(错误时):

{
  "data": {
    "createNewsletterSubscription": {
      "message": "这个邮箱已经订阅过我们的新闻邮件啦!",
      "__typename": "SubscriptionError"
    }
  }
}

这种方式让客户端能更清晰地处理成功/失败的分支逻辑,体验更流畅。


总结

  • 方案1适合快速修复,不用改动Schema,利用GraphQL原生的错误机制就能搞定;
  • 方案2更适合需要给客户端提供明确结果区分的场景,虽然要改Schema,但用户体验更好。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 10:22:47