Shane A. Stillwell
Apollo GraphQL Heuristic Fragment Problem

Apollo GraphQL Heuristic Fragment Problem

Warning! heristic fragment matching going on!

You’re happily using Apollo GraphQL and then decide, “oh, I should use an Interface or Union for this type”. That’s when the trouble starts.

I was receiving this warning in my web development console. Warning! heristic fragment matching going on!. I thought it was something I could solve in my GQL schema definitions and query files. Nope. You actually need to feed Apollo client some more information.

What’s more, a query I was using started returning { data: undefined} even though I could see the network response showing the data come back just fine.

HEADACHE!!!

First you need to generate a JSON file that holds these possibleTypes. This node file we’ll call generate-possible-types.js and run with node node ./generate-possible-types. Note, you will need node-fetch installed to do this (e.g npm i node-fetch)

const fetch = require('node-fetch');
const fs = require('fs');

fetch(`${YOUR_API_HOST}/graphql`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    variables: {},
    query: `
      {
        __schema {
          types {
            kind
            name
            possibleTypes {
              name
            }
          }
        }
      }
    `,
  }),
})
  .then(result => result.json())
  .then(result => {
    // here we're filtering out any type information unrelated to unions or interfaces
    const filteredData = result.data.__schema.types.filter(
      type => type.possibleTypes !== null,
    );
    result.data.__schema.types = filteredData;
    fs.writeFile('./fragment-types.json', JSON.stringify(result.data), err => {
      if (err) {
        console.error('Error writing fragmentTypes file', err);
      } else {
        console.log('Fragment types successfully extracted!');
      }
    });
  });

The above script generates a file called fragment-types.json looking something like

{"__schema":{"types":[{"kind":"INTERFACE","name":"Payment","possibleTypes":[{"name":"PropertyOrderPayment"},{"name":"PersonPayment"}]}]}}

Now you need to include this file in your frontend somewhere.

In the code that you setup apollo-client, you’ll need to add these lines

import introspectionQueryResultData from './fragment-types.json'
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'


const fragmentMatcher = new IntrospectionFragmentMatcher({
  introspectionQueryResultData,
})

const cache = new InMemoryCache({ fragmentMatcher })

...include the cache in your regular apollo-client middleware

Someday I’ll move away from Apollo GraphQL and just use GraphQL Tools which is much more straight forward. Until that glorious day, I’ll remain in the fight with Apollo.

References:

Image by Robin Higgins