GraphQL Fragments with Nuxt Apollo Plugin

October 9th 2020 GraphQL NuxtJS Vue.js

Apollo module for NuxtJS makes GraphQL querying very approachable even to complete GraphQL beginners. However, any query involving a union type will fail. Resolving the issue will require you to learn more about the Apollo client internals.

Basic GraphQL setup

For reference, the initial setup of the Apollo GraphQL client in a NuxtJS application consists of the following steps:

  1. Installing the Apollo module for NuxtJS and a helper library for embedding GraphQL queries in TypeScript code:

    npm i @nuxtjs/apollo graphql-tag
    
  2. Registering the module with NuxtJS and configuring the Apollo GraphQL client (both in the nuxt.config.js configuration file):

    export default {
      //...
      modules: ["@nuxtjs/apollo"],
      apollo: {
        clientConfigs: {
          default: {
            httpEndpoint: "http://localhost:3000/graphql",
          },
        },
      },
    };
    
  3. Writing the query in a TypeScript file of choice:

    import gql from "graphql-tag";
    
    export const BOOKS_QUERY = gql`
      query Books {
        books {
          ... on Ebook {
            id
            author
            title
            pages
          }
          ... on Audiobook {
            id
            author
            title
            minutes
          }
        }
      }
    `;
    
  4. Generating the type definitions for the query using the Apollo Codegen tooling (with NPM scripts):

    {
      "scripts": {
        "apollo:download": "apollo client:download-schema graphql/schema.json --endpoint http://localhost:3000/graphql",
        "apollo:generate": "apollo client:codegen --outputFlat=graphql/types/ --target typescript --includes=graphql/queries/**.ts --localSchemaFile=graphql/schema.json --tagName=gql"
      }
    }
    
  5. Executing the query (e.g. in a page or a component):

    @Component({
      async fetch(this: IndexPage) {
        const result = await this.$apollo.query<Books>({ query: BOOKS_QUERY });
        this.books = result.data.books;
      },
    })
    export default class IndexPage extends Vue {
      books: Books_books[] = [];
    }
    

Following these steps will result in the following error:

ERROR 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

Fragment matcher configuration

The error message links to the documentation for version 3.0 of the client. The NuxtJS Apollo module is still using version 2.6 which has slightly different configuration steps.

With any client version, the IntrospectionFragmentMatcher will require introspection information. The file can be generated from the GraphQL schema using the GraphQLCodeGenerator tool and its Fragment Matcher plugin.

They first need to be installed:

npm i -D @graphql-codegen/cli @graphql-codegen/fragment-matcher

Then the configuration file must be prepared:

schema: ./graphql/schema.json
overwrite: true
generates:
  ./graphql/introspection.json:
    plugins:
      - fragment-matcher

And finally, the tool can be run from an NPM script:

{
  "scripts": {
    "apollo:introspection": "graphql-codegen"
  }
}

The advanced Apollo client configuration required by the IntrospectionFragmentMatcher can't be embedded in the nuxt.config.js file. An external JavaScript file can be referenced instead:

export default {
  //...
  apollo: {
    clientConfigs: {
      default: "~/plugins/apollo-client-config.js",
    },
  },
};

This JavaScript file will contain the full Apollo client configuration (the IntrospectionFragmentMatcher and the endpoint URL that was previously specified directly in the configuration file):

import {
  IntrospectionFragmentMatcher,
  InMemoryCache,
} from "apollo-cache-inmemory";

import introspectionQueryResultData from "../graphql/introspection.json";

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

export default function (_context) {
  return {
    httpEndpoint: "http://localhost:3000/graphql",
    cache: new InMemoryCache({ fragmentMatcher }),
  };
}

With this updated configuration, union types can now be queried without errors. Of course, if the GraphQL schema changes it will need to be downloaded again and the introspection.json file might need to be regenerated. Nothing else will need to change.

A full sample, including a simple GraphQL server written in NestJS is available from my GitHub repository.

The default Apollo client configuration uses a simple heuristics-based fragment matcher that doesn't need any input from the schema but might therefore fail with more complex types and queries. This can be resolved by using a more advanced introspection-based heuristics matcher. An additional one-time configuration is required for it to work.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License