首页 > 解决方案 > Graphql/Apollo 错误有时输出 data: null 有时 data: { function: null}

问题描述

我正在为我的 graphql 设置测试。这样做时,我意识到有时错误情况下的数据对象会像这样输出:

{
   errors: [...],
   data: null
}

而有时 :

{
     errors: [...],
     data: {
        updateCity: null
    }
}

那些测试是关于突变的。这是两个代码的示例:

解析器案例1:

 updateUser(parent, args, context, info) {
      logger.debug('Mutation > updateUser resolver');
      return userController.user.update.user(parent, args.userInfo, context, info);
    },

模式案例1:

extend type Mutation {
    updateUser(userInfo: UserInfo!): User!
}

控制器案例 1:

user: async (parent, args, context, info) => {
    try {
      logger.debug('User Controller : update User');
      await controller.validate(args);
      const userModel = new UserModel('member');
      if (!(await userModel.findOne(args.id))) errorscb.userInputError('User does not exist');
      let id = args.id;
      let newArgs = args;
      delete newArgs.id;
      return userModel.updateById(id, newArgs);
    } catch (e) {
      logger.warn({ stack: e.stack, message: e.message });
      throw e;
    }
  },

模式案例 2:

extend type Mutation {

    updateCity(id: Int!, name: String, countryId: Int): City
}

解析器案例2:

updateCity(obj, args, context, info) {
      logger.info('City > updateCity resolver');
      return cityController.city.update.city(obj, args, context, info);
    },

控制器案例 2:

city: async (parent, args, context, info) => {
    try {
      logger.info('City Controller : update city');

      await controller.validate(args);
      const cityModel = new CityModel('city');
      if (!(await cityModel.findOne(args.id))) 
          errorscb.userInputError('City does not exist');
      let id = args.id;
      let newArgs = args;
      delete newArgs.id;
      return cityModel.updateById(id, newArgs);
    } catch (e) {
      logger.warn({ stack: e.stack, message: e.message });
      throw e;
    } 

我想获得一致的输出,有人知道如何解决吗?

标签: node.jsgraphqlgraphql-jsapollo-server

解决方案


这实际上是预期的行为。

updateUser和你的区别在于updateCity后者返回一个可空类型(City),而前者返回一个非空类型( User!)。响应的不同之处在于错误会在响应中传播,直到它们到达可为空的字段。从规格

如果在解析字段时抛出错误,则应将其视为该字段返回 null,并且必须将错误添加到响应中的“错误”列表中。

如果解析字段的结果为 null(因为解析该字段的函数返回 null 或因为发生错误),并且该字段是 Non-Null 类型,则抛出字段错误。必须将错误添加到响应中的“错误”列表中。

...

由于 Non-Null 类型的字段不能为 null,因此字段错误被传播以由父字段处理。如果父字段可能为空,则它解析为空,否则如果它是非空类型,则字段错误会进一步传播到它的父字段。

换句话说,通过在字段解析期间抛出错误,我们有效地将该字段解析为空。但是当我们告诉 GraphQL 一个字段具有 Non-Null 类型,并且该字段解析为 时null,GraphQL 不能返回带有null值的字段(因为这会破坏模式的契约)。所以它使整个字段为空。如果父字段也是不可为空的,则将该字段的父字段清空,依此类推……直到它到达可空字段或请求的根(data字段)。

比较:模式 1

type Query {
  a: A
}

type A {
  b: B
}

type B {
  c: String
}

模式 2

type Query {
  a: A
}

type A {
  b: B
}

type B {
  c: String!
}

模式 3

type Query {
  a: A!
}

type A {
  b: B!
}

type B {
  c: String!
}

如果我们请求字段c和字段 throws 的解析器c,则响应如下:

模式 1

{
  "data": {
    "a": {
      "b": {
        "c": null
      }
    }
  }
}

模式 2

{
  "data": {
    "a": {
      "b": null
    }
  }
}

模式 3

{
  "data": null
}

推荐阅读