Daniel Phiri Open Source Advocate. Technical Writer and Speaker. Community Lead and Builder.

Using code-gen to avoid heuristic GraphQL queries

6 min read 1849

The GraphQL logo.

I was going about my day building a Nuxt.js and GraphQL application when I got hit by an error I’ve never seen before:

You are using the simple (heuristic) fragment matcher, but your queries contain union or interface types. Apollo Client will not be able to accurately map fragments. To make this error go away, use the IntrospectionFragmentMatcher

I was aware of other GraphQL types like unions and interfaces, but beyond reading about them, this was all pretty new. Not to mention how intimidating of a word IntrospectionFragmentMatcher is.

As every developer does, I googled my error and immediately knew it was going to be a long day.

I know what you’re thinking: “Daniel.. How did you get here?”

Well, I was using a really cool self-hosted Headless CMS called Strapi (it’s GraphQL API to be specific) that has a pretty nifty feature called Dynamic Zones.

This feature lets you dynamically place content in the frontend of your application without having to worry too much about your content structure beforehand.

Still perplexed by this error message, I checked my schema and realized that the Dynamic Zone I created with Strapi was a Union type. Things started to make sense now and because I was using Apollo Client, I scoured their documentation for a solution to my problem.

They kept mentioning a certain code-gen, but the solutions they gave me were vague and barely worked.

In this article, I will explain how I got past this error message and got my queries working accurately with unions in my schema so you can too.

We made a custom demo for .
No really. Click here to check it out.

For context, Strapi Dynamic Zones let you pick from components you created and add those components to a page whenever you want to. For GraphQL, each Dynamic Zone is a Union type containing components that are defined by what you call them.

In my case, I had a Dynamic Zone called pageZone which returns a couple of components that are their own type.

Hopefully that makes everything a little clearer.

GraphQL and Union Types

GraphQL is a query language for APIs and has a lot of benefits for developers. It’s a type system, a single request for many resources, and it returns the exact data you request.

GraphQL has a schema language similar to the query language. This lets us to talk about GraphQL schemas in a language-agnostic way – meaning no matter what programming language we use, the schema language lets us communicate and access data from a GraphQL API.

Let’s distill it: GraphQL has a schema that dictates what operations and data your server can perform and return respectively. Your schema has types that describe the data you get back from your server and functionality available whenever a client connects to it.

One of these types is Unions – they are an abstract type that indicates that a field can return more than one object type, but doesn’t define specific fields itself.

What does this mean for me and my implementation? When I created my Dynamic Zone, pageZone, each component in it acts as a field. I placed three components in i t— Image, Quote, and Video, which means I had three types in my pageZone object.

union pageZone = ComponentImage | ComponentQuote | ComponentVideo

Whenever we return the pageZone type in our schema, we might get an Image, a Quote, or a Video. This changes how we make queries to begin with, and off the top of my head I would assume a query to access the data in my Dynamic Zone would look like this:

...
{
  pageZone {
     ComponentVideo {
          url
        }
     ComponentImage {
          image {
            url
            alt
          }
        }
     ComponentQuote {
          text
        }
  }
}

As much as I’d love to be able to query union types like this, the method shown above would not work at all as it isn’t supported in the GraphQL spec.

The ambiguity of Union types means we must specify the object types we want as well as the fields it contains in our query. For our scenario, the correct query would look like this:

...
{
  pageZone {
    ... on ComponentImage {
      image {
        url
        alt
      }
    }
    ... on ComponentQuote {
      text
    }
    ... on ComponentVideo {
      url
    }
  }
}

This makes the result conditional depending on what is available to us when we made the request. If we have an Image in our pageZone, it would be returned to us. Unions are useful for returning disjointed data types from a single field.

For the the sake of brevity, this is a very brief description of the types GraphQL has. If you need more detailed explanations and examples, you should check out the official GraphQL resources on schemas and types.

Now, having set up Apollo client, a problem comes up because Union types can contain all sorts of types in them. As a result, Apollo cannot be certain that a type you are requesting exists, so it may spit out an error.

Like we touched on before, our Dynamic Zone, pageZone, is a Union type. The default heuristic fragment matcher that comes with Apollo client will not work accurately when using fragments with unions, and because of this, we need to use the IntrospectionFragmentMatcher. This lets the Apollo client know in advance what your data looks like and what types it has.

The IntrospectionFragmentMatcher looks at your schema and puts out all the possible types so that when you do send a query containing Union types, Apollo client can confidently return data.

You can read into this more in the Apollo Fragments docs

To implement the IntrospectionFragmentMatcher, we’ll use a tool built by the folks at The Guild called GraphQL Code Generator.

Setting up GraphQL Code Generator

We’re implementing the IntrospectionFragmentMatcher to avoid making heuristic queries. Before we can get started, we need to install it.

In your project directory, open your terminal:

  • Run yarn add -D @graphql-codegen/cli – this installs GraphQL Code Generator
  • Run yarn add -D @graphql-codegen/fragment-matcher – this installs the fragment matcher plugin

After installing it, we can continue with our setup

  • Create a file called codegen.yml and paste this into it:
schema: "<Your GraphQL Endpoint URI>" //eg. http://localhost:1337/graphql
generates:
  ./fragmentTypes.json:
    plugins:
      - "fragment-matcher"

Remember to replace <Your GraphQL Endpoint URI> with your actual endpoint.

This YAML file acts as a config store for your code generator. You define your schema endpoint and an output directory and name for your generated introspection file. Ours is called fragmentTypes.json and can be found in the projects root directory.

Our introspection file contains the names of unions and interfaces so that your Apollo client can work accurately with the unions and their nested types in your schema.

Next up we add the code generation script to our package.json by pasting the following:

{
"scripts": {
"generate": "graphql-codegen"
}
}

This allows us to use yarn generate and get the introspection result when we need to.

Ideally, we want to set up the script to generate the introspection file whenever we build or run our application. This way, we can avoid errors if we add more types to our Union type as our introspection will always be up to date.

Now you can go ahead and run yarn generate to create your introspection file.

At this point, the introspection file is created, but we still wouldn’t be able to make accurate queries because we haven’t set up Apollo Client yet. We need to change its configuration so that it checks the introspection file for Union types and then stores them in its cache.

My Apollo client config is in ./graphql/config.js, and this can differ for different projects. Paste this code in your config file:

import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'
import introspectionResult from '~/fragmentTypes.json';

const fragmentMatcher = new IntrospectionFragmentMatcher({
    introspectionQueryResultData: introspectionResult,
  });

export default ({req, app}) => {
    return {
      httpEndpoint: process.env.BACKEND_URL || "<Your GraphQL Endpoint URI>",
      cache: new InMemoryCache({ fragmentMatcher })
    }
}

Besides the usual endpoint definition and client instantiation, this code makes it so that Apollo client reads our introspection result in ./fragmentTypes.json (from our GraphQL code-gen) and stores that in the Apollo cache.

And that’s it. Say goodbye to heuristic queries!

Rendering our query result in a Nuxt frontend

Making accurate queries is one thing. Rendering the result of them is another.

Take a Nuxt.js application for example. The code below illustrates how to achieve this:

<template>
  <div class="main">
    <div v-for="contents in content" :key="contents.id">
      <div v-for="zone in contents.pageZone" :key="zone.id" >
        
        <!-- Display all Image here -->
        <div v-if="zone.__typename === 'ComponentImage'">
          <img :src="zone.image.url" alt="zone.image.alt" />
        </div>

        <!-- Displays all Quotes here -->
        <div v-if="zone.__typename === 'ComponentQuote'">
          <p>{{ zone.text }}</p>
        </div>

        <!-- Displays all Videos here -->
        <div v-if="zone.__typename === 'ComponentVideo'">
          <a :src="zone.url"> Video Link </a>
        </div>

      </div>
    </div>
  </div>
</template>

We have our query result stored in a variable called content. We use the v-for directive to loop over the elements in it.

The Dynamic Zone (our Union type) data is stored in contents.pageZone, and you use v-for to iterate through it. As you’re probably tired of hearing, each component in the Dynamic Zone has its own type.

In the code above, we create a zone in our component to display all the Images by comparing the __typename in our union with the type of the Image component.

You get this from exploring for our schema in our GraphQL Playground.

If it matches, this lets us display the Image using zone.image.url

Similarly, with the other components in your Dynamic Zone, you can use the same approach to create zones that display data that matches their type. You can also do this for any subsequent components you add to your Dynamic Zone that you want to render in your frontend.

Understanding the logic to do it in any frontend

The config logic is pretty similar in any other frontend as it is in Nuxt. We do everything we did. However, when we’re using different frontends, our Apollo config might be in a different directory.

The core logic behind displaying this data is:

  • Looping through the query result
  • Looping through the Union type
  • Creating “zones” by conditionally displaying components. This is achieved by comparing types in the query results and types we expect to receive.

Pretty neat right?

Conclusion

GraphQL is a pretty cool tool that enables you to do a lot. We went into Union types and solved the problem of heuristic queries with Apollo client. We also set up and integrated a GraphQL Code Generator into our project repository. Hopefully, this will help you appreciate union types and the flexibility they bring.

Have you ever used Union types in a project? I’m eager to know! Hit me up on my Twitter and share your projects, favorite types or just say hi.

Until next time!

Monitor failed and slow GraphQL requests in production

While GraphQL has some features for debugging requests and responses, making sure GraphQL reliably serves resources to your production app is where things get tougher. If you’re interested in ensuring network requests to the backend or third party services are successful, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic GraphQL requests to quickly understand the root cause. In addition, you can track Apollo client state and inspect GraphQL queries' key-value pairs.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. .
Daniel Phiri Open Source Advocate. Technical Writer and Speaker. Community Lead and Builder.

One Reply to “Using code-gen to avoid heuristic GraphQL queries”

Leave a Reply