Apollo Angular Client — Unions and Interfaces

Angular: The Full Gamut Edition

Charlie Greenman
September 17, 2020
4 min read

Why I Wanted to Write This Article

I want to write this article because the approach Apollo Client uses for using Unions and Interfaces is unique and caught me by surprise. I think this 5-minute article is worth 8 to 16 hours of your time.

For the simplicity of focusing of point of this article, I will only focus on Union types


What are Union Types?

A Union type gives us the ability for one field to contain more than one field. The best way to really think of it, is in terms of Typescript. A union type would be:

type Book {
  title: String
}
type Author {
  name: String
}
type Result = Book | Author;
export type Query {
  search: [Result]
}

As we can see, it is telling us it can be one, or the other regarding the data type. So instead of returning irrelevant data, we only return the data we need.


The Benefits of Union Types

  1. It lightens payloads
  2. It allows for the UI to display backend as is (for instance, when searching, and showing potential results for categories).
  3. Also, it just doesn’t make sense sometimes

Using Union Types with Apollo Client

This is where things start to get really interesting. Using Apollo with unions/interfaces becomes tricky. In particular, the client does not understand what is going on from the server side of things. Once we have different data for a singular query coming back based on the data available, Apollo Client will just assume it is a certain type if it returns all data for a specific type. However, there are nuanced situations where this will not be the case. Apollo therefore decided to use something called the IntrospectionFragmentMatcher.

It looks something like this:

const fragmentMatcher = new IntrospectionFragmentMatcher({
  introspectionQueryResultData: {
    __schema: {
      types: [
        {
          kind: 'INTERFACE',
          name: 'User',
          possibleTypes: [
            { name: 'User' },
            { name: 'UserAsAdmin' },
            { name: 'UserAsEmployee' },
            { name: 'UserAsClient' },
          ],
        },
      ],
    },
  },
});

It tells Apollo Client for Angular that it expects the above types in the interface for User. Unfortunately, Apollo Client requires IntrospectionFragmentMatcher to be used regardless of any situation. This is very cumbersome because if the backend throws something in for an existing interface, it will cause the backend to break for that query.

I would like to repeat this once again if you do not have an IntrospectionFragmentMatcher set up in your app, and backend adds union or interface types, it will cause that data to break, and it will return an empty object.


The Dilemma — Dynamic list of introspectionQueryResultData

In their documentation, they recommend that a script be used at build time, which creates a JSON file. It is a pre-built request built into GraphQL and looks like this:

{
  __schema {
    types {
      kind
      name
      possibleTypes {
       name
      }
    }
  }
}

This JSON file should then be used in the IntrospectionFragmentMatcher.

we should note that one might immediately think, why not pull in the data for possibleTypes at runtime, and then use that for the IntrospectionFragmentMatcher? The tough part about it is that the way Apollo works for the IntrospectionFragmentMatcher, is that it requires it to be passed into the cache. It creates the cache on page load, and then having to update after the cache is loaded, well you already missed the boat. So within the context of Apollo Client for Angular, the only way to load in types is to go along with the way documentation recommends it.


What to Know Ahead of Time

What would be helpful to know ahead of time, is that this is an issue that can only be solved by DevOps. It requires that the back end and front end are built at the same time. That way, they both get pushed only once the new JSON file has been generated by the script. In this situation, there really is no way about it. Having a mono repo architecture across your company will alleviate this process. I have created this GitHub issue to bring awareness to this issue, but nothing so far.

Thank you for reading, and I really hope this helps. I honestly just did this so at least one person besides me doesn’t have to go through this the same way I did, and that perhaps it would produce some clarity around the best way to approach this issue.

More articles similar to this

footer

Razroo is committed towards contributing to open source. Take the pledge towards open source by tweeting, #itaketherazroopledge to @_Razroo on twitter. One of our associates will get back to you and set you up with an open source project to work on.