GraphQL返回MongoDB对象时非空id字段报错,求解决方案
解决GraphQL与MongoDB Aggregate返回结果的非空字段报错问题
嘿,我刚好碰到过类似的坑,让我帮你拆解开看看!
问题根源
你遇到的报错Cannot return null for non-nullable field Mail.id.,核心原因在于MongoDB的aggregate()和find()返回的数据结构差异:
model.find()返回的是Mongoose的Document实例,这类对象自带一个内置的idgetter属性,会自动把数据库里的_id(原生ObjectId类型)转换成字符串格式的id,刚好匹配你MailType里定义的id字段。- 而
model.aggregate()返回的是普通JavaScript对象,没有这个内置的idgetter。你的MailType里定义的id字段会直接从对象中读取id属性,但aggregate返回的对象只有_id,没有id,所以GraphQL解析时拿到的是null,触发了GraphQLNonNull的约束报错。
至于你打印的JSON.stringify(a) == JSON.stringify(b)结果为true,是因为JSON序列化Mongoose Document时,只会输出实际的数据库字段(也就是_id),不会包含那个内置的id getter,所以看起来两个结果一样,但实际运行时的对象结构完全不同。
解决办法
这里给你三个可行的方案,选哪个都能解决问题:
方案1:在Aggregate管道中显式生成id字段
修改你的聚合管道,通过$addFields添加字符串格式的id,再移除_id:
let a = await model.aggregate([ { $match: { isOffline: false } }, { $addFields: { id: { $toString: "$_id" } } }, // 将ObjectId转成字符串并命名为id { $unset: "_id" } // 移除原来的_id字段(可选,避免字段冗余) ]);
方案2:在Resolve函数中手动转换结果
遍历aggregate返回的数组,给每个对象添加id字段:
return a.map(item => ({ ...item, id: item._id.toString() // 把ObjectId转成字符串格式的id }));
方案3:修改MailType的id字段解析逻辑
直接在MailType的id字段里指定resolve函数,从_id取值并转换:
const MailType = new GraphQLObjectType({ name: 'Mail', fields: () => ({ id: { type: new GraphQLNonNull(GraphQLID), resolve: (parent) => parent._id.toString() // 手动从_id解析出id }, // 其他字段... }) })
这样不管是aggregate还是find返回的结果,都能正确解析出符合要求的id字段啦!
内容的提问来源于stack exchange,提问作者yusung lee
相关产品推荐
相关产品推荐

