How To Condense Your GraphQL Queries With the @materializer Directive

Lucia Cerchie - Mar 25 '21 - - Dev Community

Originally posted on StepZen

Let's start by understanding our context. StepZen's @materializer is a custom GraphQL directive, but what's a graphQL directive?

As the GraphQL specs tell us,

"A GraphQL schema describes directives which are used to annotate various parts of a GraphQL document as an indicator that they should be evaluated differently by a validator, executor, or client tool such as a code generator."

Basically, this means that we can change what we get back from our queries based on what's in the schema, but this will make more sense with an example. Let's look at the @skip directive. It's used only in GraphQL operations:

query yellowQuery($isCerulean: Boolean) {
  yellowField @skip(if: $isCerulean)
}
Enter fullscreen mode Exit fullscreen mode

In this example, yellowField is returned when $isCerulean is false.

Core specs vs. custom

@skip is one of just three default directives defined in the GraphQL spec. @include and @deprecated are also default.

StepZen's @materializer is a custom directive, in this case made by StepZen. So why did we make it?

@materializer reduces the code you need to write

Say we had a database with two tables, genus and species, and we wanted to make a query like:

{
  species(name: "p. tigris") {
    name
    discoveredDate
    genus {
      name
      isLethal
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This means we're grabbing code from both tables, based on a species_id row in our database. How are we going to implement this?

What you would have to write without @materializer

Without the @materializer directive, you have to write your own resolverMap:

const resolver = {
  Query: {
    genus(parent, args, context, info) {
    if (!args) return new Error('missing species_id');
      return genus.find(genus => genus.id === args.id);
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

Already you've had to construct logic to pick out the right genus, as well as think through how you'll handle errors.

But what if your data was coming from two different backends-- say, the genus from a MySQL database and the species from an API. You'd have to write resolvers to combine the data yourself. That's a lot of work.

The amount of code you write with @materializer

Here, name is the argument and field is how @materializer will fill it.

  @materializer(
        query: "genus"
        arguments: [{ name: "id" field: "genusID"}]
  )
Enter fullscreen mode Exit fullscreen mode

Pop it in at the end of your type extension, and you're done! @materializer combines your data from your two types for you, even if the types are requesting the data from two different sources.

So much simpler than writing your own resolvers.

Where do we go from here?

Curious about how to build a GraphQL API that connects to a MySQL database with StepZen? Try this blog post out.

Want to know more about connecting backends in general? See our docs.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .