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
相关产品推荐
相关产品推荐

