Aug 27, 2019

Using Directives in Scaled Apollo GraphQL

A bit ago I wrote about scaling an Apollo GraphQL Server using schema federation. Read that here.

Directives are basically annotations that can be added to any line of a schema, which start up some processing that should be done related to that line. For instance, you can deprecate fields or queries, honor a list of roles needed to view a field, or transform a field. All you have to do is add an annotation on that line in the schema. It’s pretty amazing.

Using Apollo Gateway, the ability to use directives was left out, for now. Understandably too. Apollo Gateway magically orchestrates distributed services of GraphQL schema/resolvers, and somehow allows them to reference each other. See how to create that magic in the blog I wrote, and mentioned earlier. So, that being said, directives were normally a global set of resolvers for any schema to use; So how can we make that work in a distributed system of services?

The monolithic way is to stitch all of our schemas and resolvers into a big ol’ blob, like this:

import { SchemaDirectiveVisitor } from "graphql-tools";

const typeDefs = `
type ExampleType {
  newField: String
  oldField: String @deprecated(reason: "Use \`newField\`.")
}`;

const schema = makeExecutableSchema({
  typeDefs,
  schemaDirectives: {
    deprecated: DeprecatedDirective
  }
});

class DeprecatedDirective extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    field.isDeprecated = true;
    field.deprecationReason = this.args.reason;
  }

  public visitEnumValue(value: GraphQLEnumValue) {
    value.isDeprecated = true;
    value.deprecationReason = this.args.reason;
  }
}

The normal way of creating directives is by building a class, override some functions, and inject that into the Apollo instance. Any time that the directive is visited in the schema, your directive class will be invoked and your function will run, allowing you to transform the field in any way.

Okay, so, to create a distributed set of Apollo services that can use directives, we can’t go the normal route. Unfortunately we can’t inject schemaDirectives into buildFederatedSchema, like we did with makeExecutableSchema. The buildFederatedSchema function ignores the schemaDirectives object, and I believe that is because Apollo has not yet settled on the way that they intend to enable directives in federated schemas. To get the job done, we have to rely on an old feature of Apollo that allows us to attach directives to a schema. Import and use the attachDirectiveResolvers as shown below:

const {attachDirectiveResolvers} = require('graphql-tools');
const typeDefs = gql `
directive @upper on FIELD_DEFINITION
... rest of schema ...
`;
const directiveResolvers = {
    upper(next, src, args, context) {
        return next().then((str) => {
            if (typeof(str) === 'string') {
                return str.toUpperCase();
            }
            return str;
        });
}
const schema = buildFederatedSchema([
    {
        typeDefs,
        resolvers,
    }
]);
attachDirectiveResolvers(
    schema,
    directiveResolvers,
);
const server = new ApolloServer({
    schema
});

About the Author

Corey Webster profile.

Corey Webster

Sr. Consultant
Leave a Reply

Your email address will not be published. Required fields are marked *

Related Blog Posts
Structuring SwiftUI Previews for API Calls
SwiftUI, together with Combine and Xcode 11+, provide a powerful toolset for quickly creating an app with a native UI. In Xcode 11+, the preview pane was introduced in order to provide live snapshots of […]
Seamlessly Integrating Micro Apps with iFrame
A recent client wanted to upgrade a small portion of their legacy application with a more modern UI and extra functionality, like fuzzy text search. There are a few approaches to incremental upgrades of legacy […]
Consul on Docker EE
[markdown]”Consul is a service networking solution to connect and secure services across any runtime platform and public or private cloud” – [https://consul.io](https://consul.io) This post details a way to run a Consul cluster on Docker EE […]
Passing the AWS Machine Learning Speciality Exam
The Amazon Machine Learning Specialty Exam is a 3-hour, 65 question test. It is designed to test your skills in AWS specific Data Engineering and Machine Learning Practices along with Machine Learning in general. I […]