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.
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 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.
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:
yarn add -D @graphql-codegen/cli
– this installs GraphQL Code Generatoryarn add -D @graphql-codegen/fragment-matcher
– this installs the fragment matcher pluginAfter installing it, we can continue with our setup
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!
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.
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:
Pretty neat right?
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!
LogRocket is like a DVR for web and mobile 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. Start monitoring for free.Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
3 Replies to "Using code-gen to avoid heuristic GraphQL queries"
Saved my day! Thanks
Thank you very much! I was prepared for the worse when I saw that error arise.
Note that apollo client 3 has removed IntrospectionFragmentMatcher and replaced it with a system for specifying possibleTypes for unions and interfaces. The examples in this post no longer work.