GraphQL自定义指令,无需在架构中显式声明

时间:2018-10-16 19:03:51

标签: node.js graphql apollo-server

我正在尝试实现自定义GraphQL指令。我的理解是,如果我的SchemaDirectiveVisitor子类实现了static getDirectiveDeclaration(directiveName, schema),那么我不需要可以在我的SDL(架构定义语言)中手动声明该指令。

  

由于AuthDirective实现了getDirectiveDeclaration,因此架构编写者不再需要在架构中明确包含指令@auth ...声明。返回的GraphQLDirective对象将用于强制使用参数类型和默认值,并使诸如GraphiQL之类的工具能够使用架构自省功能来发现指令。此外,如果AuthDirective类无法实现visitObject或visitFieldDefinition,则会抛出一个有用的错误。

     

来源:https://blog.apollographql.com/reusable-graphql-schema-directives-131fb3a177d1

  

但是,如果您要实现供公众使用的可重用SchemaDirectiveVisitor,则您可能不是编写SDL语法的人,因此您可能无法控制模式作者决定声明哪些指令以及如何声明。这就是为什么实施良好,可重用的SchemaDirectiveVisitor应该考虑覆盖getDirectiveDeclaration方法

     

来源:https://www.apollographql.com/docs/apollo-server/features/creating-directives.html

在我的代码中,尽管已实现static getDirectiveDeclaration(directiveName, schema),但仍必须在SDL中声明该指令。

如果不在SDL中手动声明就不能使用吗?

完整的示例代码:

const { ApolloServer, gql, SchemaDirectiveVisitor } = require('apollo-server');
const { DirectiveLocation, GraphQLDirective, defaultFieldResolver } = require("graphql");

class UpperCaseDirective extends SchemaDirectiveVisitor {
  static getDirectiveDeclaration(directiveName, schema) {
    console.log("inside getDirectiveDeclaration", directiveName)
    return new GraphQLDirective({
      name: directiveName,
      locations: [
        DirectiveLocation.FIELD_DEFINITION,
      ],
      args: {}
    });
  }

  visitFieldDefinition(field) {
    console.log("inside visitFieldDefinition")
    const { resolve = defaultFieldResolver } = field;
    field.resolve = async function (...args) {
      const result = await resolve.apply(this, args);
      if (typeof result === 'string') {
        return result.toUpperCase();
      }
      return result;
    };
  }
}

const books = [
  {
    title: 'Harry Potter and the Chamber of Secrets',
    author: 'J.K. Rowling',
  },
  {
    title: 'Jurassic Park',
    author: 'Michael Crichton',
  },
];

const typeDefs = gql`

  #########################################
  # ONLY WORKS WITH THIS LINE UNCOMMENTED #
  #########################################
  directive @upper on FIELD_DEFINITION

  type Book {
    title: String
    author: String @upper
  }

  type Query {
    books: [Book]
  }
`;

const resolvers = {
  Query: {
    books: () => books,
  },
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  schemaDirectives: {
    upper: UpperCaseDirective
  }
});

server.listen().then(({ url }) => {
  console.log(`  Server ready at ${url}`);
});

1 个答案:

答案 0 :(得分:1)

我遇到了同样的问题,并且能够从graphql-tools issue #957找到此评论。

  

来自changelog

     
    

注意:graphql 14包含重大更改。我们推出了graphql-tools的主要版本来适应那些重大更改。如果您打算将graphql 14与graphql-tools 4.0.0一起使用,请确保已查看了graphql重大更改列表。

  
     

这很可能是由于graphql-js现在要求您在尝试使用指令之前在其架构中定义指令的缘故。例如:

directive @upper on FIELD_DEFINITION

type TestObject {
  hello: String @upper
}
     

您可以通过在架构中预定义指令来解决此问题,但我想确认一下。如果可行,我们将需要更新文档。