GraphQL 突变为嵌套响应返回 null

Posted

技术标签:

【中文标题】GraphQL 突变为嵌套响应返回 null【英文标题】:GraphQL mutation is returning null for nested responses 【发布时间】:2019-11-25 13:19:17 【问题描述】:

我有 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,用于将用户连接到关联的公司对象

用户变异

    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.emailBaseUrlauthentication/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 不能在突变期间绘制我的嵌套对象,但可以通过查询来做到这一点。

【问题讨论】:

Daniel Reardan 可能会用他的解释链接来回答这个问题:***.com/questions/56319137/…。我了解到 GraphQL 消息非常笼统,问题可能是众多问题之一。最好从 Daniel 的解决方案开始。 @Preston 感谢您的回复,我会检查一下 您说您在查询时成功检索了公司信息。您如何检索给定用户的公司数据?如果您使用标准 DB 连接进行此操作,那么您的 newUser 数据可能不完整并且不包含 company 信息。理想情况下,您希望为 company 类型的 company 字段提供一个特定的解析器,并以这种方式实现您的关联;这将保证在您返回类型 User 时解析器始终运行(当然,如果查询了 company 字段)。 【参考方案1】:

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

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

    创建 - 插入新行时,您可以特别包含与 Sequelize 的创建或构建方法的关联。像这样:
let client = new Client(
      
        ...params
      ,
       include: [ClientGroup] 
    );

return client.save()

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

    更新 - 这个有点棘手,因为关联不在正在变异的模型中。所以返回的对象不会包含那些关联。此外,Sequelize 的更新方法没有像我们第一次创建对象时那样提供包含模型关联的选项。这是一个快速的解决方案:
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 ,
        );

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

虽然这解决了问题,但肯定还有其他方法可以解决这个问题。 特别是,如果您想直接通过此模型改变这些关联。如果是这种情况,我建议查看 Sequelize 的 transactions。

【讨论】:

以上是关于GraphQL 突变为嵌套响应返回 null的主要内容,如果未能解决你的问题,请参考以下文章

使用输入对象的graphql突变

嵌套查询/解析器时的 GraphQL.js 订阅响应错误

Apollo GraphQL 中带有变量的嵌套查询

是否可以将 Apollo GraphQL 响应映射为单一类型?

graphql 突变不返回嵌套字段

GraphQL 简单嵌套查询返回空字段