如何使用Apollo Server 2.0 GraphQL将架构指令中的信息作为返回数据包括在内?

时间:2019-03-29 03:37:19

标签: error-handling graphql apollo-server

我正在使用架构指令对字段进行授权。解析程序返回后,Apollo服务器将调用指令。因此,这些指令无法访问输出,因此当授权失败时,如果没有复杂的解决方法抛出错误,我就无法为用户提供相关信息,最终会抛出错误,无论查询是否请求,错误总是返回错误数据。 / p>

我希望有人比我更了解Apollo的内部结构,并指出在哪里可以从指令中插入适当的信息,因此不必破坏GraphQL的标准功能。

我尝试将输出包含在上下文中,但是尽管该指令具有访问权限,但是该操作不起作用,因为解析器已经返回了数据,并且此后不需要上下文版本。

截至目前,我在指令中使用代码DIRECTIVE_ERROR抛出了一个自定义错误,并包含了我想返回给用户的消息。在formatResponse函数中,我查找指令错误并通过将错误数组传输到数据的内部错误数组中来过滤错误数组。我知道formatResponse并不用于修改数据的内容,但是据我所知,这是我唯一可以访问所需内容的地方。同样令人沮丧的是,响应中的错误对象没有包含错误中的所有字段。

type User implements Node {
  id: ID!
  email: String @requireRole(requires: "error")
}

type UserError implements Error {
  path: [String!]!
  message: String!
}

type UserPayload implements Payload {
  isSuccess: Boolean!
  errors: [UserError]
  data: User
}

type UserOutput implements Output {
  isSuccess: Boolean!
  payload: [UserPayload]
}

/**
 * All output responses should be of format:
 * {
 *  isSuccess: Boolean
 *  payload: {
 *    isSuccess: Boolean
 *    errors: {
 *      path: [String]
 *      message: String
 *    }
 *    data: [{Any}]
 *  }
 * }
 */
const formatResponse = response => {
  if (response.errors) {
    response.errors = response.errors.filter(error => {
      // if error is from a directive, extract into errors
      if (error.extensions.code === "DIRECTIVE_ERROR") {
        const path = error.path;
        const resolverKey = path[0];
        const payloadIndex = path[2];

        // protect from null
        if (response.data[resolverKey] == null) {
          response.data[resolverKey] = {
            isSuccess: false,
            payload: [{ isSuccess: false, errors: [], data: null }]
          };
        } else if (
          response.data[resolverKey].payload[payloadIndex].errors == null
        ) {
          response.data[resolverKey].payload[payloadIndex].errors = [];
        }

        // push error into data errors array
        response.data[resolverKey].payload[payloadIndex].errors.push({
          path: [path[path.length - 1]],
          message: error.message,
          __typename: "DirectiveError"
        });
      } else {
        return error;
      }
    });

    if (response.errors.length === 0) {
      return { data: response.data };
    }
  }

  return response;
};

我对Apollo中操作顺序的理解是:

解析器返回数据
根据查询参数过滤数据吗?
指令在应用的对象/字段上调用
根据查询参数过滤数据吗?
formatResponse有机会修改输出
formatError有机会修改错误
返回客户

我想要的是不必在指令中引发错误,从而可以通过在formatResponse中提取信息来创建传递给用户的信息。预期结果是客户端仅接收其请求的字段,但是当前方法将其中断,并返回数据错误和所有字段,无论客户端是否请求它们。

1 个答案:

答案 0 :(得分:0)

您可以使用destruct注入它:

const { SchemaDirectiveVisitor } = require("apollo-server-express");

const { defaultFieldResolver } = require("graphql");

const _ = require("lodash");

class AuthDirective extends SchemaDirectiveVisitor {
  visitFieldDefinition(field) {
    const { resolve = defaultFieldResolver } = field;

    field.resolve = async function (parent, args, context, info) {
      // You could e.g get something from the header
      //
      // The verification below its necessary because
      // my application runs locally and on Serverless
      const authorization = _.has(context, "req")
        ? context.req.headers.authorization
        : context.headers.Authorization;

      return resolve.apply(this, [
        parent,
        args,
        {
          ...context,
          user: { authorization, name: "", id: "" }
        },
        info,
      ]);
    };
  }
}

然后在解析器上,您可以通过context.user访问它。