Graphql/Apollo 错误输出有时 data: null 有时 data: { function: null}

Gii*_*iZz 3 node.js graphql graphql-js apollo-server

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

{
   errors: [...],
   data: null
}
Run Code Online (Sandbox Code Playgroud)

有时 :

{
     errors: [...],
     data: {
        updateCity: null
    }
}
Run Code Online (Sandbox Code Playgroud)

这些测试是针对突变的。这是两个代码的示例:

解析器案例 1:

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

模式案例 1:

extend type Mutation {
    updateUser(userInfo: UserInfo!): User!
}
Run Code Online (Sandbox Code Playgroud)

控制器案例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;
    }
  },
Run Code Online (Sandbox Code Playgroud)

模式案例 2:

extend type Mutation {

    updateCity(id: Int!, name: String, countryId: Int): City
}
Run Code Online (Sandbox Code Playgroud)

解析器案例 2:

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

控制器案例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;
    } 
Run Code Online (Sandbox Code Playgroud)

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

Dan*_*den 5

这实际上是预期的行为。

\n

updateUseryour和your之间的区别updateCity在于,后者返回可空类型 ( City),而前者返回非空类型 ( User!)。响应中的差异来自以下事实:错误会向上传播响应,直到到达可为 null 的字段为止。从规格来看

\n
\n

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

\n

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

\n

...

\n

由于非空类型字段不能为空,因此字段错误将传播到父字段来处理。如果父字段可能为 null,则它解析为 null,否则如果它是非 Null 类型,则字段错误将进一步传播到它的\xe2\x80\x99s 父字段。

\n
\n

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

\n

比较:架构 1

\n
type Query {\n  a: A\n}\n\ntype A {\n  b: B\n}\n\ntype B {\n  c: String\n}\n
Run Code Online (Sandbox Code Playgroud)\n

图式2

\n
type Query {\n  a: A\n}\n\ntype A {\n  b: B\n}\n\ntype B {\n  c: String!\n}\n
Run Code Online (Sandbox Code Playgroud)\n

图式3

\n
type Query {\n  a: A!\n}\n\ntype A {\n  b: B!\n}\n\ntype B {\n  c: String!\n}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

图式1

\n
{\n  "data": {\n    "a": {\n      "b": {\n        "c": null\n      }\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

图式2

\n
{\n  "data": {\n    "a": {\n      "b": null\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

图式3

\n
{\n  "data": null\n}\n
Run Code Online (Sandbox Code Playgroud)\n