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

GraphQL中MembershipType的user字段报错问题求助

问题根源与解决方案

嘿,这个错误的原因很明确——你碰到了GraphQL类型循环引用时的定义顺序问题!

为什么会报错?

看一下你的代码:你先定义了MembershipType,然后才定义UserType。当JavaScript执行到MembershipType里的user: { type: new GraphQLNonNull(UserType) }这一行时,UserType还没有被定义,此时它的值是undefined。而GraphQLNonNull要求必须传入一个有效的、可空的GraphQL类型,不能是undefined,这就触发了你看到的Expected undefined to be a GraphQL nullable type错误。

另外,我还注意到你代码里有个小笔误:UserType中memberships字段的resolve函数里,ctx的类型写成了Content,应该是Context,这个也顺手帮你修正了~

两种可行的解决方案

方案1:调整定义顺序 + 提前声明变量

我们可以先声明MembershipType变量,然后定义UserType,最后再给MembershipType赋值。这样既能解决循环引用问题,又能保证类型定义时引用的都是已存在的对象:

// 提前声明MembershipType变量,让UserType可以引用它
let MembershipType;

const UserType = new GraphQLObjectType({
  name: 'User',
  interfaces: [nodeInterface],
  fields: {
    id: globalIdField(),
    firstName: {
      type: GraphQLString,
      resolve(parent) {
        return parent.first_name;
      },
    },
    lastName: {
      type: GraphQLString,
      resolve(parent) {
        return parent.last_name;
      },
    },
    email: {
      type: GraphQLString,
      resolve(parent) {
        return parent.email;
      },
    },
    memberships: {
      type: new GraphQLList(MembershipType),
      resolve(parent, args, ctx: Context) {
        return ctx.membershipsByUserId.load(parent.id);
      },
    },
    membershipsCount: {
      type: new GraphQLNonNull(GraphQLInt),
      resolve(parent, args, ctx: Context) {
        return ctx.userMembershipsCount.load(parent.id);
      },
    },
  },
});

// 此时UserType已经完全定义,MembershipType可以安全引用它
MembershipType = new GraphQLObjectType({
  name: 'Membership',
  interfaces: [nodeInterface],
  fields: {
    id: globalIdField(),
    user: {
      type: new GraphQLNonNull(UserType),
      resolve(parent, args, ctx: Context) {
        return ctx.userById.load(parent.user_id);
      },
    },
    role: {
      type: new GraphQLNonNull(GraphQLString),
    },
    team: {
      type: GraphQLString,
    },
    createdAt: {
      type: new GraphQLNonNull(GraphQLString),
      resolve(parent) {
        return parent.created_at;
      },
    },
    updatedAt: {
      type: new GraphQLNonNull(GraphQLString),
      resolve(parent) {
        return parent.updated_at;
      },
    },
  },
});

// 根据你的导出需求调整,比如分别导出
export default UserType;
export { MembershipType };

方案2:用函数延迟解析字段

另一种更灵活的方式是把fields定义成函数,而不是直接的对象。函数会在所有类型都初始化完成后才执行,这样内部引用的类型都是有效的,完全不用关心定义顺序:

const MembershipType = new GraphQLObjectType({
  name: 'Membership',
  interfaces: [nodeInterface],
  // 用函数返回字段对象
  fields: () => ({
    id: globalIdField(),
    user: {
      type: new GraphQLNonNull(UserType),
      resolve(parent, args, ctx: Context) {
        return ctx.userById.load(parent.user_id);
      },
    },
    role: {
      type: new GraphQLNonNull(GraphQLString),
    },
    team: {
      type: GraphQLString,
    },
    createdAt: {
      type: new GraphQLNonNull(GraphQLString),
      resolve(parent) {
        return parent.created_at;
      },
    },
    updatedAt: {
      type: new GraphQLNonNull(GraphQLString),
      resolve(parent) {
        return parent.updated_at;
      },
    },
  }),
});

const UserType = new GraphQLObjectType({
  name: 'User',
  interfaces: [nodeInterface],
  // 同样用函数返回字段对象
  fields: () => ({
    id: globalIdField(),
    firstName: {
      type: GraphQLString,
      resolve(parent) {
        return parent.first_name;
      },
    },
    lastName: {
      type: GraphQLString,
      resolve(parent) {
        return parent.last_name;
      },
    },
    email: {
      type: GraphQLString,
      resolve(parent) {
        return parent.email;
      },
    },
    memberships: {
      type: new GraphQLList(MembershipType),
      resolve(parent, args, ctx: Context) {
        return ctx.membershipsByUserId.load(parent.id);
      },
    },
    membershipsCount: {
      type: new GraphQLNonNull(GraphQLInt),
      resolve(parent, args, ctx: Context) {
        return ctx.userMembershipsCount.load(parent.id);
      },
    },
  }),
});

export default UserType;
export { MembershipType };

额外说明

你提到仅为Membership配置了Mutation未配置Query,这和当前的报错无关,不用纠结这一点~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:02:54