GraphQL关联查询无法返回用户数据,报非空字段null错误
解决Cannot return null for non-nullable field User.email的问题
我帮你排查了代码,问题主要出在Mongoose模型定义不匹配和事件创建时未正确关联用户这两个地方,具体如下:
1. 核心问题:事件模型的creator字段类型错误
你的event.js里把creator定义成了数组类型:
creator: [ { type: Schema.Types.ObjectId, ref: 'User' } ]
但在GraphQL的Event类型里,creator被声明为单个User!(非空的单个用户)。当你用populate('creator')查询时,得到的是一个用户数组,而不是单个用户对象。这就导致GraphQL尝试从数组中读取email字段时失败,返回null,违反了User.email的非空约束,触发了错误。
2. 修复步骤
步骤1:修正Event模型的creator字段
打开event.js,把creator改成单个ObjectId类型(因为一个事件应该只有一个创建者):
const eventSchema = new Schema({ title: { type: String, required: true }, description: { type: String, required: true }, price: { type: Number, required: true }, date: { type: Date, required: true }, // 去掉数组括号,改成单个关联 creator: { type: Schema.Types.ObjectId, ref: 'User', required: true } });
步骤2:完善createEvent的Mutation逻辑
你当前的rootValue里缺少createEvent的实现,而且即使有,也没有把事件和用户关联起来。补充如下代码(注意:实际项目中你需要通过身份验证获取当前用户ID,这里先以测试为例):
rootValue: { events: () => { return Event.find() .populate('creator') .then(events => { return events.map(event => { // 现在creator是单个用户对象,解构后能正确获取email return { ...event._doc, _id: event.id }; }); }) .catch(err => { throw err; }); }, createEvent: async (args) => { // 1. 先找到一个已存在的用户(实际项目中应该从请求上下文获取登录用户) const existingUser = await User.findOne({ email: "test@example.com" }); if (!existingUser) { throw new Error("用户不存在,请先创建用户!"); } // 2. 创建事件并关联用户ID const event = new Event({ title: args.eventInput.title, description: args.eventInput.description, price: parseFloat(args.eventInput.price), date: new Date(args.eventInput.date), creator: existingUser._id // 关联创建者的用户ID }); // 3. 保存事件并更新用户的createdEvents列表 const savedEvent = await event.save(); existingUser.createdEvents.push(savedEvent._id); await existingUser.save(); // 4. 返回事件数据,同时确保creator字段正确关联 return { ...savedEvent._doc, _id: savedEvent.id, creator: existingUser }; }, createUser: async (args) => { // 补充你的createUser逻辑(注意密码要加密) const hashedPassword = await bcrypt.hash(args.userInput.password, 12); const user = new User({ email: args.userInput.email, password: hashedPassword }); return user.save() .then(result => { // 注意:不要返回明文密码,这里可以把password设为null或者不返回 return { ...result._doc, _id: result.id, password: null }; }) .catch(err => { throw err; }); } }
步骤3:优化User类型的GraphQL定义(可选但重要)
你的User类型里返回password字段是不安全的,建议把它改成非必填或者直接移除:
type User { _id: ID! email: String! # 移除password,或者改成password: String createdEvents: [Event!] }
3. 测试验证
- 先通过GraphiQL执行
createUsermutation创建一个用户:
mutation { createUser(userInput: { email: "test@example.com", password: "123456" }) { _id email } }
- 再执行
createEventmutation创建一个关联该用户的事件:
mutation { createEvent(eventInput: { title: "Test Event", description: "A test event", price: 99.99, date: "2024-12-31T23:59:59Z" }) { _id title creator { email } } }
- 最后执行你的查询:
query { events { creator { email } } }
这时候应该能正确返回用户的email,不会再出现null错误了。
内容的提问来源于stack exchange,提问作者pl-jay
相关产品推荐
相关产品推荐

