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
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]