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
Why we started using JSON Schema in our internal Helm charts
Helm 3 supports validating the provided chart values against a JSON Schema. While it may be quicker to get started in your chart development without a schema, we found it valuable for a number of […]
Rewriting files in Google Cloud Storage
Rewriting Files in GCP Note: even though this code is in Python, this should be the same idea in JavaScript, Go, etc. I wrote the following to copy a file from one Google Cloud Storage […]
Interpreting Spatial Data in the Age of COVID-19
As 2020 has come to an end, many are eager to leave the mess of COVID-19 behind with the new year and gain a fresh start. Unfortunately, new cases are still soaring across the United […]
Building a Better Mousetrap
Recently, my daughter (age 6) was into building “mousetraps” out of shoe boxes. These were more or less comfortable cardboard mouse houses complete with beds, rooms, everything a mouse could want or need and not […]