Apollo is one of the most popular React library to work with GraphQL APIs. However they don’t have native support when your APIs contain union types.
Let’s look at an example API that contains union types and how we can work with them using Apollo. Assuming you have an API like this:
{
search(text: "an") {
__typename
... on Human {
name
height
}
... on Droid {
name
primaryFunction
}
... on Starship {
name
length
}
}
}
that returns this response….
{ "data": { "search": [ { "__typename": "Human", "name": "Han Solo", "height": 1.8 }, { "__typename": "Human", "name": "Leia Organa", "height": 1.5 }, { "__typename": "Starship", "name": "TIE Advanced x1", "length": 9.2 } ] } }
If you use the above query via Apollo, you will see this warning messages in the browser console:
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` as described in the docs: https://www.apollographql.com/docs/react/advanced/fragments.html#fragment-matcher
This is because Apollo by default uses a simple heuristics matching to match the response string into the Response
object. We need to do some extra work to tell Apollo how to parse the response with union types.
Apollo has a separate section on their docs about this.
Step 1: Save the information about the fragment types in a separate file:
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('./fragmentTypes.json', JSON.stringify(result.data), err => { if (err) { console.error('Error writing fragmentTypes file', err); } else { console.log('Fragment types successfully extracted!'); } }); });
The JSON file should look something like this:
{ "__schema": { "types": [ { "kind": "UNION", "name": "UnionSearchDetails", "possibleTypes": [ { "name": "Human" }, { "name": "Droid" }, { "name": "Starship" } ] } ] } }
Step 2: Create fragmentMatcher
using the JSON file from the previous step:
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import introspectionQueryResultData from './fragmentTypes.json'; const fragmentMatcher = new IntrospectionFragmentMatcher({ introspectionQueryResultData });
Step 3: Now include this fragmentMatcher
in cache
object and pass it to the Apollo client:
import ApolloClient from 'apollo-client'; import { InMemoryCache } from 'apollo-cache-inmemory'; import { HttpLink } from 'apollo-link-http'; // add fragmentMatcher code from step 2 here const cache = new InMemoryCache({ fragmentMatcher }); const client = new ApolloClient({ cache, link: new HttpLink(), });
Now all done. The warnings and errors that you noticed previously should have gone away and the Response
object from Apollo should contain the correct data.
How to use ‘Union types’ in tests and storybooks?
Now that you have fixed the problem for production code, how can we fix it for your tests or storybooks?
Apollo provides MockedProvider
for this purpose. Here is a sample:
<MockedProvider mocks={mocks} addTypename cache={cache}>
<MyComponenent />
</MockedProvider>
The important change here from the sample code is that addTypename
has to be true, to allow Apollo to use the __typename
in the response to map the object to the correct type.
MOST COMMENTED
Flutter
Flutter Setup
React Native
Learn React Native with a Board Game (Part 1 of 4)
jQuery / Web Development
jQuery DataTable: Sorting dynamic data
Uncategorized
Hibernate – Associations are not loaded
Database / Java / MySQL / Spring Boot
Hibernate Error – Encountered problem trying to hydrate identifier for entity
Spring Boot / Uncategorized
Working with Hibernate in a multi-threaded application
Web Development
Designing REST APIs