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

Posted

技术标签:

【中文标题】如何使用 Apollo Server 2.0 GraphQL 将模式指令中的信息作为返回的数据包含在内?【英文标题】:How to include info from schema directives as returned data using Apollo Server 2.0 GraphQL? 【发布时间】:2019-08-19 23:09:18 【问题描述】:

我正在使用模式指令对字段进行授权。 Apollo 服务器在解析器返回后调用指令。因此,指令无权访问输出,因此当授权失败时,如果没有复杂的解决方法抛出错误,无论查询是否请求它们,最终总是返回错误数据。

我希望有人比我更了解 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】:

你可以使用 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 访问它。

【讨论】:

以上是关于如何使用 Apollo Server 2.0 GraphQL 将模式指令中的信息作为返回的数据包含在内?的主要内容,如果未能解决你的问题,请参考以下文章

Apollo-Server,如何与 SailsJS 一起使用?

如何使用 Apollo Client 虚拟字段

如何使用 apollo-server 进行缓存

如何在 React Apollo 2.0 中正确地重新获取和缓存更新的数据?

使用带有 NextJS 的 Apollo Client 时服务器端渲染数据?

如何使用 Express 和 Apollo-Server 获取 HTTP 授权标头