DEV Community

Cover image for Gatsby with WPGraphQL, ACF and Gatbsy-Image
Henrik Wirth for NeverNull

Posted on • Updated on

Gatsby with WPGraphQL, ACF and Gatbsy-Image

So, there is a lot happening around Gatsby with WordPress and WPGraphQL. At times, it can get confusing and a lot of given functionality is hard to find or figure out. I am working on a client project right now and somehow had to handle images, that I source through my WordPress backend.

After trying out all sorts of solutions and working through the plugin library of Gatsby, I ended up finding a little piece of documentation, that led me to a solution, that works for me. And, because some very helpful people in the WPGraphQL Slack Chat asked me to write about that solution ... here I am.

I simply wanted to have my WordPress Media as static files inside my Gatsby app and work with the gatsby-image plugin for transformation/optimization.

Libraries used


Gatsby Config

/* --------- gatsby-config.js --------- */

module.exports = {

  plugins: [

    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/assets/images`,
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: "gatsby-source-graphql",
      options: {
        typeName: "WPGraphQL",
        fieldName: "wpgraphql",
        url: `https://${YOUR_DOMAIN}/graphql`,
      },
    },
  ]
}

Enter fullscreen mode Exit fullscreen mode
  • Make sure all the plugins are mentioned correctly in your config file
  • For some reason, it can cause errors not having gatsby-source-filesystem used in the configs. Make sure it resolves to a valid path, even if there are no images inside the folder.

Gatsby Node


/* --------- gatsby-node.js --------- */

const { createRemoteFileNode } = require(`gatsby-source-filesystem`)

exports.createResolvers = async (
  {
    actions,
    cache,
    createNodeId,
    createResolvers,
    store,
    reporter,
  },
) => {
  const { createNode } = actions

  await createResolvers({
    WPGraphQL_MediaItem: {
      imageFile: {
        type: "File",
        async resolve(source) {
          let sourceUrl = source.sourceUrl

          if (source.mediaItemUrl !== undefined) {
            sourceUrl = source.mediaItemUrl
          }

          return await createRemoteFileNode({
            url: encodeURI(sourceUrl),
            store,
            cache,
            createNode,
            createNodeId,
            reporter,
          })
        },
      },
    },
  })
}
Enter fullscreen mode Exit fullscreen mode

That's really all you need. Let's break it down a little:

  • WPGraphQL_MediaItem: This depends on your config. It starts with the typeName of your gatsby-source-graphql.
  • createRemoteFileNode gives you the ability to pull in remote files and automatically adds them to your schema.
  • imageFile: will be the type you can query (see below).
  • type: 'File': will add the MediaItems as Files, which is great, because now gatsby-image can make use of it.
  • url: encodeURI(sourceUrl): Here we encode the Url coming from WordPress, to make sure it is parsed correctly even if the image paths include umlauts.

Pages Query


# page.js

query {
  WPGraphQL {
    pages {
      nodes {
        featuredImage {
          sourceUrl
          imageFile {
            childImageSharp {
              fixed {
              ...GatsbyImageSharpFixed
              }
            }
          }
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Important: Always query sourceUrl, otherwise it doesn't work, because the resolver would miss that data.

Final thoughts

Thats all there is to it. And it works with ACF Media files too. Same same.

Almost ... I think it is important to mention, that with this method all available Media-Files coming from WordPress will be pulled in the cache. So, if there is, for some reason, a clear of the cache happening, it would need to pull them in again I guess. For performance, it could still be more useful to somehow fetch all the Media-Files apart from the Gatbsy-Cache. That would mean though, you have to do the hard work of creating the schema yourself. I'm sure there will be great solutions for that too. Would be happy if someone has some experience with that and would share their thoughts :)

Caching

If your server doesn't support etags, the default caching of the gatsby-source-filsystem won't work.

You can checkout the custom solution by Robert Marshall here, which should be slightly faster and doesn't rely on etags: https://thoughtsandstuff.com/gatsby-with-wordpress-caching-downloaded-media-images-to-reduce-build-time/

Coming Up

I plan on writing up a big article about setting up Gatbsy + WordPress + ACF + Polylang for a client ready project with Multilanguage-Support and dynamic content creation through ACF's flexible content fields. I wonder if that would be of interest. Any thoughts and suggestions are highly appreciated.

References

See the Gatsby documentation of it here: https://www.gatsbyjs.org/docs/schema-customization/#feeding-remote-images-into-gatsby-image

Discussion (29)

Collapse
robmarshall profile image
Robert Marshall

I needed my build to cache images so I adapted your resolver. I have written a walk through - hopefully it can help you like you helped me!

thoughtsandstuff.com/gatsby-with-w...

Collapse
parthp1808 profile image
Parth Patel

Anyhow, we can implement this kind of cache for posts as well? Rebuilding unchanged pages increases build time for larger blogs

Collapse
henrikwirth profile image
Henrik Wirth Author

You can use the new gatsby-source-wordpress@v4 which already does a lot of work for you when it comes to incremental builds.

github.com/gatsbyjs/gatsby-source-...

Collapse
henrikwirth profile image
Henrik Wirth Author

Super nice. If I may I'll reference your article, so people won't miss on this.

Collapse
robmarshall profile image
Robert Marshall

No worries. Just link back!

Thread Thread
henrikwirth profile image
Henrik Wirth Author

Hey Robert, I was wondering as I am trying your caching solution, isn't gatsby-source-filesystem already caching the images? I just tested it and for me it already caches the them.

Thread Thread
robmarshall profile image
Robert Marshall

I was contacted by Kyle Mathews saying the same thing. In the majority of situations Gatsby seems to cache and check itself. My version is 'slightly' faster, and handles the unlikely edge case of servers not using etags.

The conversation: twitter.com/robertmars/status/1172...

I suppose it depends on your environment. I quite like skipping the server checks, as it seems like an extra step.

Thread Thread
henrikwirth profile image
Henrik Wirth Author • Edited

Interesting. Also I guess it could be useful to write some custom caching, to persist the image-cache over gatsby. So if gatsby clears its cache, it will still be there. This would be interesting for peeps with gigabytes of Media-Files on their WordPress, who want to make sure, that they never ever have pull everything in again.

I'll update some info in this post. Thanks for the explanation.

Collapse
byronwade profile image
Byron Wade • Edited

I'm not exactly sure why but I did everything to the tee but my gatsby-nodes.js file loops over creating pages multiple times. I narrowed it down to the script you made. I'm a bit confused about it though.

Collapse
zanami profile image
zanami • Edited

Same here. I'm actually using this caching method.
gasby develop runs the same create post/download image tasks 4 or 5 times

gasby build runs only once (i.e. works ok)

Caching doesn't seem to work either, gatsby still downloads images but maybe not featuredImage ones as I'm also using gatsby-wpgraphql-inline-images

Collapse
henrikwirth profile image
Henrik Wirth Author

What versions are you using? I haven't tested it with the newest versions of all the plugins and Gatsby. So maybe something changed? Also the node Version could be interesting. If you have a reproduction on Github, that would help a lot.

I don't have so much time on my hands at the moment for the next two months. But I'll try to help as much as I can.

Thread Thread
zanami profile image
zanami

I forgot to mention I'm using this rather outdated demo from gatsby-wpgraphql-inline-images author (I believe). It's based on the official wpgraphql demo which is also outdated, as is their demo.wpgraphql.com, probably due to older wpgraphql plugin

I'm going to start over with your code @ part 6 as I don't need ACF stuff.
Looks good so far, apart from dealing with nonexistent isFrontPage field.

Thanks for your great work!

Thread Thread
henrikwirth profile image
Henrik Wirth Author

isFrontPage should be available in the newer versions of WPGraphQL. Make sure in WordPress, that there is a page actually set to be the front page.

wordpress.org/support/article/crea...

Collapse
benoitdubuc profile image
benoitdubuc • Edited

Great post. It helped us a lot to extract images. We were wondering if this could also be used for other types of binary files such as mp3, mp4 or pdf.

Interested in the Polylang too. We have a project where we will use WPML so I am sure it will help and if we find different points, we will share.

Collapse
henrikwirth profile image
Henrik Wirth Author • Edited

Heya, thanks. Glad it helped.

mp3, mp4 and pdf should be the same process. You just have to figure out what kind of media-type it is and then write a resolver for it.

I actually started of with WPML and for some reasons it got super messy. Thats why I switched to Polylang. Would be interesting to see a solution with WPML though.

Collapse
benoitdubuc profile image
benoitdubuc

Will sure share with you. Project should start in the next week.

Collapse
robmarshall profile image
Robert Marshall

I recently had to build this functionality, so wrote a short article on it. Hopefully can point you in the right direction!

thoughtsandstuff.com/pulling-ordin...

Collapse
danlaw86 profile image
Dan Lawson

Hi, i've tried to get this working using the exact same code but am receiving the following error:

Error: Schema must contain uniquely named types but contains multiple types named "File".

Do you know why this might be happening at all? thanks

Collapse
henrikwirth profile image
Henrik Wirth Author

I remember someone having the same problem. He needed to make sure, that gatsby-source-filesystem is used in the config and the options contain a valid folder path.

If this is not the problem, feel free to come to the WPGraphQL Slack Chat. You can chat with me and others there and we can try to find the bug with you.

Collapse
arielnt1993 profile image
Ariel Navarro

question, beause woth this when I pull a page and render it with gatsby develop (or gatsby build and serve) the images that are on the page will source from the wp site. what happens if the site get taken down? how do I change the src of the images from the pulled information? or even better download the images and use those instead of relying on another page

Collapse
henrikwirth profile image
Henrik Wirth Author

Not sure if I understand the question correctly.

So with this functionality above, you source the images from your Backend on build/develop and it will generate static files into your build folder.

So if you take that build and deploy it, the images will still be there even if your Backend is down.

Now if you will use gatsby build again while your backend is down, that obviously doesn't work. If you want your images to be persitent in your frontend without the need of your backend, just add them to an image folder inside your gatbsy project.

Collapse
arielnt1993 profile image
Ariel Navarro

right sorry yhea it was the wrong question (I was kinda sleepy) but thanks anyway

Collapse
michalhonc profile image
michalhonc

Awesome! Thanks

Collapse
jacobarriola profile image
Jacob Arriola

This is great. Thank you - working as expected!

I wonder how I can bring in author avatars in the same manner as well (instead of just 3rd-party calls to gravatar's servers at run time)?

Collapse
henrikwirth profile image
Henrik Wirth Author • Edited

I just tried this:

createResolvers({
    WPGraphQL_Avatar: {
      imageFile: {
        type: "File",
        resolve(source) {
          return createRemoteFileNode({
            url: source.url,
            store,
            cache,
            createNode,
            createNodeId,
            reporter,
          })
        },
      },
    },
  })

Seems to work for me. Obviously it will fetch gravatars at build time. Maybe another Avatar solution would be possible, if you want to avoid using Gravatars.

Collapse
demonloki2 profile image
loki

I can't understand what exactly you are grabbing here. Remote files? If so, why you use gatsby-source-filesystem?

Collapse
henrikwirth profile image
Henrik Wirth Author

How else what you grab remote files (from WordPress) and source them in Gatsby?

This by the way is before the new experimental wordpress source plugin, that already does the job for you.

Collapse
robmarshall profile image
Robert Marshall

Thanks so so much for this article! I had no idea how to go about this. Great write up!

(and yes, I am interested in Polylang)

Collapse
silencerweb profile image
Maksim Gorodov

Definitely interested in the article on how to set up Gatbsy + WordPress + ACF + Polylang :)