Skip to content

Instantly share code, notes, and snippets.

@monachilada
Last active October 16, 2020 19:03
Show Gist options
  • Star 32 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save monachilada/af7e92a86e0d27ba47a8597ac4e4b105 to your computer and use it in GitHub Desktop.
Save monachilada/af7e92a86e0d27ba47a8597ac4e4b105 to your computer and use it in GitHub Desktop.
Sample gatsby-config.js enabling live preview in Craft CMS
const { createHttpLink } = require('apollo-link-http');
const fetch = require('node-fetch');
const store = require('store');
const sourceNodes = require('gatsby/dist/utils/source-nodes');
require('dotenv').config();
const craftGqlUrl = process.env.CRAFT_GQL_URL;
const craftGqlToken = process.env.CRAFT_GQL_TOKEN;
module.exports = {
plugins: [
{
resolve: 'gatsby-source-graphql',
options: {
typeName: 'Craft',
fieldName: 'craft',
createLink: () =>
createHttpLink({
uri: `${craftGqlUrl}`,
headers: {
Authorization: `Bearer ${craftGqlToken}`,
},
fetch: (uri, options) => {
const token = store.get('X-Craft-Token');
return fetch(
`${uri}${token !== undefined ? `?token=${token}` : ''}`,
options,
);
},
}),
},
},
],
developMiddleware: app => {
app.use('*', (req, res, next) => {
if (req.query.token) {
store.set('X-Craft-Token', req.query.token);
sourceNodes();
}
next();
});
},
};
@provedev
Copy link

provedev commented Mar 6, 2020

Getting the following error: UNHANDLED REJECTION Schema must contain uniquely named types but contains multiple types named "Craft".

Any ideas?

@anthonyferreol
Copy link

Have the same problem here. I think sourceNodes is only called during the bootstrap sequence but cannot be used to "update" the GRAPHQL schema. Any ideas?

@monachilada
Copy link
Author

Unfortunately one of the Gatsby updates released since I first posted this changed something in the sourceNodes sequence with the consequence being this code is no longer working. Unfortunately I haven’t had the time to dig into exactly why. I would be grateful for any tips and would happily update the snippet.

However, long term I’ve begun to move away from this solution as it was never going to scale well on larger sites, since EVERY query needs to be rerun. I’ve resigned myself to waiting for the official Gatsby plugin which is in the works.

@anthonyferreol
Copy link

This do it: (based on the refresh function from the POST api (__refresh): with webHook if needed (can be removed)

createSchemaCustomization({ refresh: true }).then(() => {
    sourceNodes({ webhookBody });
});

@provedev
Copy link

provedev commented Mar 9, 2020

Thanks. How exactly do I implement this?

@Digital-E
Copy link

Thanks for your solution @anthonyferreol, it's working for me!

@wfendler
Copy link

So, I kind of got this working. I definitely need a deeper understanding of middleware and how createLink and createHttpLink work to finish this up but perhaps someone else on here could help me out.

By adding createSchemaCustomization and fixing the sourceNodes reference I was able to get this close. The problem here is that when Craft loads this in the iframe it doesn't load the latest draft. If refetchInterval is set however, it will load the correct content whenever that interval fetches new data. So, for now I just have the interval set to 30 seconds so content editors don't see changes immediately but they rarely have to wait long to see updates.

Does anyone else see what's wrong in here? We're hosting this on Heroku, running gatsby develop -p $PORT -H 0.0.0.0 (based on this article by @andreaskeller)

const { createHttpLink } = require("apollo-link-http");
const fetch = require("node-fetch");
const store = require("store");
const sourceNodes = require("gatsby/dist/utils/source-nodes").default;
const createSchemaCustomization = require("gatsby/dist/utils/create-schema-customization")
  .createSchemaCustomization;
require("dotenv").config();

const craftGqlUrl = process.env.CRAFT_GQL_URL;
const craftGqlToken = process.env.CRAFT_GQL_TOKEN;

module.exports = {
  plugins: [
    {
      resolve: "gatsby-source-graphql",
      options: {
        typeName: "Craft",
        fieldName: "craft",
        refetchInterval: 30,
        createLink: () =>
          createHttpLink({
            uri: `${craftGqlUrl}`,
            headers: {
              Authorization: `Bearer ${craftGqlToken}`,
            },
            fetch: (uri, options) => {
              const token = store.get("X-Craft-Token");
              return fetch(
                `${uri}${token !== undefined ? `?token=${token}` : ""}`,
                options
              );
            },
          }),
      },
    },
  ],
  developMiddleware: (app) => {
    app.use("*", (req, res, next) => {
      if (req.query.token) {
        // Reject app-data.json requests, they don't contain a token and override 
        // the page request that sets the token
        if (!req.baseUrl.includes("app-data.json")) {
          store.set("X-Craft-Token", req.query.token);
          createSchemaCustomization({ refresh: true }).then(() => {
            // This function only works if I pass {} or { webhookBody: null }
            sourceNodes({});
          });
        }
        // next() to moves on to the next middleware in the stack
        // http://expressjs.com/en/api.html#app.use
        next();
      }
    });
  },
};

@jamie-l-robertson
Copy link

jamie-l-robertson commented Oct 3, 2020

@wfendler Did you manage to figure out your latest draft issue, I have hit the same problem...

@wfendler
Copy link

wfendler commented Oct 7, 2020

@jamie-l-robertson I haven't. After making several attempts I've given up until we have better Gatsby + Craft support through a new source plugin. It seems like this is coming soon: https://github.com/craftcms/craft-gatsby

Discord conversation starts around here: https://discordapp.com/channels/456442477667418113/629094175509446657/752907415003070514

@jamie-l-robertson
Copy link

@wfendler Ah excellent, thanks for the heads up!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment