Reputation: 1208
I am trying to implement a custom GraphQL directive. My understanding is that if my SchemaDirectiveVisitor
subclass implements static getDirectiveDeclaration(directiveName, schema)
then I don't have to manually declare the directive in my SDL (Schema Definition Language).
Because AuthDirective implements getDirectiveDeclaration, it’s no longer necessary for the schema author to include the directive @auth ... declaration explicitly in the schema. The returned GraphQLDirective object will be used to enforce the argument types and default values, as well as enabling tools like GraphiQL to discover the directive using schema introspection. Additionally, if the AuthDirective class fails to implement visitObject or visitFieldDefinition, a helpful error will be thrown.
Source: https://blog.apollographql.com/reusable-graphql-schema-directives-131fb3a177d1
and
However, if you’re implementing a reusable SchemaDirectiveVisitor for public consumption, you will probably not be the person writing the SDL syntax, so you may not have control over which directives the schema author decides to declare, and how. That’s why a well-implemented, reusable SchemaDirectiveVisitor should consider overriding the getDirectiveDeclaration method
Source: https://www.apollographql.com/docs/apollo-server/features/creating-directives.html
In my code, despite having implemented static getDirectiveDeclaration(directiveName, schema)
I still have to declare the directive in SDL.
Shouldn't it work without manually declaring in SDL?
Full Example Code:
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}`);
});
Upvotes: 0
Views: 4054
Reputation: 26
I have the same problem and was able to find this comment from graphql-tools issue #957.
From the changelog:
NOTE: graphql 14 includes breaking changes. We're bumping the major version of graphql-tools to accommodate those breaking changes. If you're planning on using graphql 14 with graphql-tools 4.0.0, please make sure you've reviewed the graphql breaking changes list.
This is likely caused by the fact that
graphql-js
now requires you to define your directives in your schema, before you attempt to use them. For example:directive @upper on FIELD_DEFINITION type TestObject { hello: String @upper }
You can likely work around this by pre-defining your directives in your schema, but I'd like to confirm this. If this works, we'll need to update the docs.
Upvotes: 1