GraphQL突变为嵌套响应返回null

时间:2019-07-16 18:30:26

标签: graphql sequelize.js apollo

我有2个模型,公司和用户。从数据库角度来看,公司有许多用户。创建单个用户时,我想通过返回与该用户关联的公司来利用graphQL的功能。但是,这仅在执行查询时有效。尝试进行突变时,对象会发生突变,但是请求的关系数据始终返回null

在模型中,我们声明一个->多关系,并将公司模型架构包含在用户模型架构中以访问数据

用户模型架构

  type User {
    clients: [Client!]
    company: Company         <------- Company Relation
    companyId: UUID
    confirmed: Boolean
    defaultPortfolioSize: Int
    email: String!
    firstName: String!
    lastLogin: String!
    lastName: String!
    id: UUID!
    isActive: Boolean
    isStaff: Boolean
    isSuperuser: Boolean
    password: String
    phoneNumber: String
    priceNotification: Boolean
    priceThreshold: Float
    sentimentNotification: Boolean
    sentimentThreshold: Float
    token: String

    clientCount: Int
    notificationCount: Int
    portfolioCount: Int
    stockAverageCount: Float
    totalValue: Float
    stockList: [PortfolioStock!]
  }

在用户突变中,我们传递一个公司ID,该ID用于将用户连接到关联的公司对象

用户突变

    user(
      companyId: UUID               <---- Company ID for relation
      confirmed: Boolean
      defaultPortfolioSize: Int
      delete: Boolean
      email: String
      firstName: String
      lastName: String
      id: UUID
      isActive: Boolean
      isStaff: Boolean
      isSuperuser: Boolean
      password: String
      phoneNumber: String
      priceNotification: Boolean
      priceThreshold: Float
      sentimentNotification: Boolean
      sentimentThreshold: Float
      username: String
    ): User!

解析器非常简单。我们会验证授权,然后继续请求。

用户突变解析器

  user: async (_, params, { user }) => {
    if (params.id) {
      await authorize(user, Permission.MODIFY_USER, { userId: params.id });
    } else {
      // Anyone can register
    }

    return await userDataLoader.upsertUser(user, params);
  },

神奇的地方就是数据加载器。我们调用upsertUser创建,更新和删除任何对象。在这里,我们成功创建了一个用户,并且可以在数据库中验证创建。

用户数据加载器

upsertUser: async (user, params) => {
    ...

    /* Register  */

    if (!params.companyId) {
      throw new UserInputError("Missing 'companyId' parameter");
    }

    if (!params.password) {
      throw new UserInputError("Missing 'password' parameter");
    }

    let newUser = new User({
      billingAddressId: 0,
      dateJoined: new Date(),
      defaultPortfolioSize: 45,
      isActive: true,
      isStaff: false,
      isSuperuser: false,
      lastLogin: new Date(),
      phoneNumber: '',
      priceNotification: false,
      priceThreshold: 0,
      sentimentNotification: false,
      sentimentThreshold: 0,
      subscriptionStatus: false,
      ...params,
    });

    newUser = await newUser.save();
    newUser.token = getJWT(newUser.email, newUser.id);

    EmailManager(
      EmailTemplate.CONFIRM_ACCOUNT,
      `${config.emailBaseUrl}authentication/account-confirmation/?key=${
        newUser.token
      }`,
      newUser.email
    );

    return newUser;
  },

  // Including the users query dataloader for reference
  users: async params => {
    return await User.findAll(get({ ...defaultParams(), ...params }));
  },

这是一个示例变体,其中我们创建一个用户对象并请求带有嵌套公司关系的响应。

示例变异

mutation {
  user(
    companyId: "16a94e71-d023-4332-8263-3feacf1ad4dc",
    firstName: "Test"
    lastName: "User"
    email: "test@gmail.com"
    password: "PleaseWork"
  ) {
    id
    company {
      id
      name
    }
    email
    firstName
    lastName
  }
}

但是,当请求将关系包含在响应对象中时,api返回null而不是对象。

示例响应

ACTUAL:
{
  "data": {
    "user": {
      "id": "16a94e71-d023-4332-8263-3feacf1ad4dc",
      "company": null,
      "email": "test@gmail.com",
      "firstName": "Test",
      "lastName": "User"
    }
  }
}

EXPECTED:
{
  "data": {
    "user": {
      "id": "16a94e71-d023-4332-8263-3feacf1ad4dc",
      "company": {
        "id": "16a94e71-d023-4332-8263-3feacf1ad4dc",
        "name": "Test Company",
      },
      "email": "test@gmail.com",
      "firstName": "Test",
      "lastName": "User"
    }
  }
}

我想我对为什么graphQL在突变期间无法绘制嵌套对象但可以通过查询进行绘制感到困惑。

1 个答案:

答案 0 :(得分:0)

问题出在Sequelize。由于表的突变不与其关联共享,因此突变对象不像典型查询那样包含所述关联。因此,从突变对象请求的任何关联都将返回null,因为该对象不直接存在于模型中。

话虽这么说,有几种方法可以补充这个问题...

  1. 创建-插入新行时,可以专门将关联与Sequelize的create或build方法一起使用。像这样:
let client = new Client(
      {
        ...params
      },
      { include: [ClientGroup] }
    );

return client.save()

使用方法中的options参数,我们可以将include参数与关联的模型一起传递。这将随关联一起返回。

  1. 更新-这有点棘手,因为同样,关联不在被突变的模型内。因此,返回的对象将不包含那些关联。而且,Sequelize的update方法没有像我们第一次创建对象那样提供包含模型关联的选项。这是一个快速的解决方案:
await Client.update(params, {
          // @ts-ignore: Unknown property 'plain'
          plain: true,
          returning: true,
          where: { id: params.id },
        });

        return await Client.findOne({
          include: [ClientGroup],
          where: { id: params.id },
        });

首先,我们使用update方法对对象进行变异。更新后,我们使用findOne方法来获取具有关联的变异对象。

尽管这可以解决问题,但是肯定还有其他方法可以解决此问题。 特别是,如果您想直接通过此模型对那些关联进行变异。如果是这种情况,我建议您查看Sequelize的transactions