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.

Related Blog Posts
Building Better Data Visualization Experiences: Part 1 of 2
Through direct experience with data scientists, business analysts, lab technicians, as well as other UX professionals, I have found that we need a better understanding of the people who will be using our data visualization products in order to build them. Creating a product utilizing data with the goal of providing insight is fundamentally different from a typical user-centric web experience, although traditional UX process methods can help.
Kafka Schema Evolution With Java Spring Boot and Protobuf
In this blog I will be demonstrating Kafka schema evolution with Java, Spring Boot and Protobuf.  This app is for tutorial purposes, so there will be instances where a refactor could happen. I tried to […]
Redis Bitmaps: Storing state in small places
Redis is a popular open source in-memory data store that supports all kinds of abstract data structures. In this post and in an accompanying example Java project, I am going to explore two great use […]
Let’s build a WordPress & Kernel updated AMI with Packer
First, let’s start with What is an AMI? An Amazon Machine Image (AMI) is a master image for the creation of virtual servers in an AWS environment. The machine images are like templates that are configured with […]