1

I am relatively new to using Craft Headlessly and am trying to query matrix fields with Nuxt JS and it's Apollo module.

I have discovered, that although the graphQL queries work in the GraphiQl interface, they do not work with Apollo due to Apollo not knowing the schema for unknown fragments. I've stumbled apon a few others who have had this issue such as this guy but their solution confused me and seemed more complicated than I would expect. I'm sure there must be a simple solution considering this is a widely used system for working with Craft Headlessly.

I also saw this response from someone (see the response from Mylesthe.dev) who gave some instruction but I still didn't quite understand how to apply this. The post above this also mentions that Apollo-client 3.0 should be able to handle unknown fragment types but I'm on @nuxtjs/apollo ^v4 and still experiencing it.

Does anyone have a simple clear fix for this that I can apply to my set up, so that I can query matrix fields with multiple fragment types in Craft CMS?

My Package.json file is:

{
  "name": "nuxtjs-test",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate"
  },
  "dependencies": {
    "@nuxtjs/apollo": "^4.0.1-rc.5",
    "core-js": "^3.6.5",
    "dotenv": "^8.2.0",
    "flickity": "^2.2.2",
    "headroom.js": "^0.12.0",
    "node-fetch": "^2.3.0",
    "nuxt": "^2.14.6",
    "vue-rellax": "^0.2.0"
  },
  "devDependencies": {
    "@nuxtjs/dotenv": "^1.4.1",
    "@nuxtjs/style-resources": "^1.0.0",
    "fibers": "^5.0.0",
    "sass": "^1.32.0",
    "sass-loader": "^10.1.0"
  }
}

My apollo GQL query is (const GET_HOMEPAGE):

{
  entry(section: "homepage") {
    ... on homepage_homepage_Entry {
      homepageBuilder {
        ... on homepageBuilder_sectionHeadline_BlockType {
          typeHandle
          headline
        }
        ... on homepageBuilder_showcase_BlockType {
          typeHandle
          limit
        }
        ... on homepageBuilder_articles_BlockType {
          typeHandle
        }
      }
    }
  }
}

my apollo smart query is:

apollo: {
      entry: {
        query: GET_HOMEPAGE,
        error (error) {
            this.error = JSON.stringify(error.message);
            console.log(error);
        },
        result ({ data, loading, networkStatus }) {
          console.log("[networkStatus]",networkStatus);
          console.log(data);
        },
      },
    },

my apollo settings in Nuxt.config file is:

  apollo: {
    clientConfigs: {
      default: {
        httpEndpoint: process.env.APOLLO_DEFAULT_ENDPOINT,
      }
    },
    defaultOptions: {
      $query: {
        fetchPolicy:'no-cache'
      }
    }
  },

2 Answers 2

2

I struggled with this for ages, but after @mylesthe.dev (who responded to one of the posts i mention above) spoke to me directly to provide fantastic support and some examples, I figured it out. So for anyone else still struggling like I was, here's the code (thanks to his work) which finally got things working for me:

First of all, in your nuxt.config.js set up your apollo configs:

// Apollo config and endpoint for graph ql
  apollo: {
    includeNodeModules: true,
    clientConfigs: {
      default: '@/apollo/client-configs/default.js' // This is where you'll set up the client and import the possible fragment types
    }
  },

Now we create the apollo client set up with the fragment schema file (which we'll create) in apollo/client-configs/default.js

import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import schema from './schema.json';
const fragmentMatcher = new IntrospectionFragmentMatcher({
    introspectionQueryResultData: schema
  })

export default ({req, app}) => {
    const token = process.env.GRAPHQL_TOKEN
    return {
        httpEndpoint: process.env.API_ENDPOINT,
        getAuth: () => `Bearer ${token}`, // remove if you're using the public schema
        cache: new InMemoryCache({ fragmentMatcher }),
    }
}


Now save an empty schema.json file in apollo/client-configs/.

Next we need to set up the script to query and generate this schema on nuxtServerInit. You'll need fs to write your schema file. You can install it with NPM: npm install --save fs.

Once installed, go back to your nuxt.config and add fs to the build:

  build: {
    extend (config, ctx) {
      config.node = {
        fs: 'empty'
      }
    }
  }

Then in your store/index.js:

import Vuex from 'vuex';
import fetch from 'node-fetch';
import fs from 'fs';


const createStore = () => {
  return new Vuex.Store({
    actions: {
        async nuxtServerInit({commit}, {app}) {

            // only update fragements locally
            if (process.env.NODE_ENV == 'local') {
                 
                // LOAD FRAGMENT TYPES AND STORE IN FILE
                // APOLLO READS THIS FILE LATER
                fetch(process.env.API_ENDPOINT, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', authorization: 'Bearer ' + process.env.GRAPHQL_TOKEN, },
                    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('./apollo/client-configs/schema.json', JSON.stringify(result.data), err => {
                        if (err) {
                            console.error('Error writing fragmentTypes file', err);
                        }
                    });
                });

            }
            
        },
    }
  });
};

export default createStore

Your schema should now be generated locally to the schema file and that file will be stored in the apollo cache.

1

For Apollo client 3.x users

Apollo client now uses possibleTypes instead of IntrospectionFragmentMatcher

You can manage schema.json as per other answer, but then use as such:

const possibleTypes = {}
schema.data.__schema.types.forEach((supertype) => {
  if (supertype.possibleTypes) {
    possibleTypes[supertype.name] = supertype.possibleTypes.map((subtype) => subtype.name)
  }
})

And to initialise client:

return new ApolloClient({
...
  cache: new InMemoryCache({
    possibleTypes,
    ...
  }
})

References:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.