Contentful with GatsbyJS

GatsbyJS

Contentful with GatsbyJS

Hooking your GatsbyJS app with the Contentful CMS

This article will guide you through getting your Gatsby website up and running with Contentful. For this article I will start from scratch and end up with GatsbyJS building pages with posts in markdown from Contentful and have your site running on a Netlify server accessible via the Internet.

Installing our Gatsby App

If you are just starting out with Gatsby you should try out the tutorials on their site and browse the documentation. One of the benefits of choosing Gatsby is excellent documentation and plugins available on their site which can be easily searched when you need assistance. I will assume you have installed Gatsby previously and are ready to start a new project. So let's begin by creating a new project in the Terminal:

:~ $> gatsby new gatsby-blog
:~ $> cd gatsby-blog
~:gatsby-blog $> gatsby develop

This will install and start the Gatsby development server, giving us two important URI's - the site to view at http://localhost:8000/ and GraphiQL IDE at http://localhost:8000/___graphql to explore the app's data including the contentful API when we install the plugins later.

Setting up Contentful

Now that we have our Gatsby development server running, Let's head to Contentful and create a new account. Their Micro tier is free and more than adequate for most purposes with 24 content types and 5,000 records. Signing up creates a space for us, we need to go to 'Content model' and start a new model called 'Blog'. We will create five fields for our blog post - Title, Content, Image, Published and Slug. The Blog model should now look like this:

Screen Shot 2018-06-22 at 11.02.34 am

Now we have our content model setup, let's click on the content tab and add two simple sample -blog posts (sometimes Gatsby's GraphQL query will fail when building via the gatsby develop command with only one item in the Contentful model). Fill out the fields and publish the content so it is available via the API for Gatsby. The slug field will be used on Gatsby as part of the URI, so it must be unique and not contain spaces. There is only one thing left to on the Contentful site, click the Settings drop down menu and select API keys.

Screen Shot 2018-06-22 at 11.26.23 am

Once you are at this page name add a name and description, and save the changes. Leave this page open for later, we will need to add the Space ID and access token keys to the Gatsby config file to allow our app to access the sample post we made.

Setting up Gatsby with Contentful

Let's head back to the Terminal and shut down the development server with ctrl + c and install the additional packages to allow us to connect to Contentful and display the blog post on the site.

~:gatsby-blog $> npm install gatsby-source-contentful gatsby-transformer-remark gatsby-image dotenv

The plugin gatsby-source-contentful is what allows us to connect with Contentful's API and get our data, env allows us to save the Contentful API keys in a file that is hidden from GitHub whilst allowing us to use them later on Netlify. The other two plugins will help us format that data into HTML for our site.

Now we need to open the gatsby-config.js file in the root of your project folder to add in the the packages to the Gatsby and the configuration we need:

//gatsby-config.js
require('dotenv').config();

module.exports = {
  siteMetadata: {
      title: 'Gatsby Default Starter',
  },
  plugins: [
      {
          resolve: `gatsby-source-contentful`,
          options: {
            spaceId: process.env.SPACE_ID,
            accessToken: process.env.ACCESS_TOKEN,
          },
      },
      'gatsby-transformer-remark'
  ],
};

In this file we required the dotenv module to allow us to load our environment variables from a file into the config without exposing the API secret in our GitHub/GitLab repository. Even if your repository is private it is still a very good idea not to publish them on the repository. For this to work we need to create a new .env file in the root and add our Content Delivery API and Space ID into the file like so from Contentful page we had open earlier (they should look similar to the ones below, but the ones shown are only for example):

SPACE_ID=qibx4j8lfwen
ACCESS_TOKEN=46bc6f77364a9c7abd66b0032560c0dc6daf703f414a1f74a3cb8abh3y8j22kl

Then save the file and add this to our .gitignore so it won't be commited into git in the next commit. The file should look like this:

# Project dependencies
.cache
node_modules
yarn-error.log
.env

# Build directory
/public
.DS_Store

It's now time to start the server again with gatsby develop and see if we've been successful in connecting Contentful API to our app. If you've added the keys correctly the server will start up and it's time to go to to http://localhost:8000/___graphql and check the queries from Contentful are being returned correctly. Let's try and get our new blog post data from GraphiQL. On the left panel enter the following query and ctrl + enter to run the query. If you are trying to build a query the GraphiQL allows us to auto complete with ctrl + space.

{ 
  allContentfulBlog {
    edges {
      node {
        title
        content {
          childMarkdownRemark {
            excerpt
          }
        }
        slug
      }
    }
  }
}

In the right side, you should see the returned blog post in JSON format that you made in Contentful, a sample is shown below. The allContentful fields all have the content model's name after it, such as allContentfulProject or allContentfulAdvertisement. This is where the Contentful model name comes into play. Since we want our blog posts, the one we want to use is allContentfulBlog to match our Blog content model.

Screen Shot 2018-06-22 at 3.50.21 pm

Now let's get Gatsby building our blog posts for us. We will need to close the server again with ctrl + c and add the GraphQL query to the site. First we need to modify gatsby-node.js like so:

//gatsby-node.js
const path = require('path');

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions;
  return new Promise((resolve, reject) => {
    const blogPostTemplate = path.resolve('src/templates/blog-post.js');
    resolve(
      graphql(`
        {
            allContentfulBlog (limit:100) {
                edges {
                    node {
                        slug
                    }
                }
            }
        }
          `).then((result) => {
        if (result.errors) {
          reject(result.errors);
        }
        result.data.allContentfulBlog.edges.forEach((edge) => {
          createPage({
            path: edge.node.slug,
            component: blogPostTemplate,
            context: {
              slug: edge.node.slug,
            },
          });
        });
        return;
      }),
    );
  });
};

This finds the slug of each blog post and creates a page for each blog using the React component at 'src/templates/blog-post.js'. Now we need to make that file so we can restart the server and let it build the blog pages for us.

//src/templates/blog-post.js
import React from 'react';
import PropTypes from 'prop-types';
import { graphql } from 'gatsby';
import Img from "gatsby-image";

const BlogPost = (props) => {
  const { title, published, bannerImage, content } = props.data.contentfulBlog
  return (
    <div>
      <h2 style={{
        borderBottom: '2px solid #efefef',
        paddingBottom: '8px',
        textAlign: 'center'
      }}>
        {title}
      </h2>
      <p style={{
        borderBottom: '1px solid #efefef',
        paddingBottom: '8px',
        textAlign: 'center'
      }}>
        {published}
      </p>
      <div>
        <Img fluid={bannerImage.fluid} />
      </div>
      <hr />
      <div dangerouslySetInnerHTML={{ __html: content.childMarkdownRemark.html }} />
    </div>
  )
};

BlogPost.PropTypes = {
  data: PropTypes.object.isRequired
}
;
export default BlogPost;

export const pageQuery = graphql`
    query blogPostQuery($slug: String!){
        contentfulBlog(slug: {eq: $slug}) {
            title
            published(formatString: "MMMM DD, YYYY")
            bannerImage {
                fluid(maxWidth: 400) {
                    aspectRatio
                    sizes
                    src
                    srcSet
                }
            }
            content {
                childMarkdownRemark {
                    html
                }
            }
        }
    }
`

On this file we run another GraphQL query, taking the slug passed from previous query to search the API for the blog we are looking for. We then request all the data in this query that we need to build this blog post and then we can access it via props within the React component. We can then use it to build our JSX for the page like a normal React component.

Let's see is we were successful. Using gatsby develop we will restart our server, during the start sequence the GraphQL queries will run and the pages will be built, if we were successful the process will complete without any errors. If you browse to http://localhost:8000/ you will notice no difference, however in development mode if we view a page that does exist such as http://localhost:8000/nopage Gatsby will list the available pages like so:

Screen Shot 2018-06-22 at 4.28.35 pm

From here we can click on the link to view the blog post:

Screen Shot 2018-06-22 at 4.31.17 pm

And success, our blog post has been created within our site. It's styling is basic but it's working. An important note is Gatsby will cache parts of the API data during the development build, so if you were to add more fields to the Content model later it might require you to clear the cache by rm -rf .cache/ public within the Terminal then restarting the server. Now we only need add some links for our blog on the index page so we can navigate to each post:

//src/pages/index.js
import React from 'react';
import { graphql } from 'gatsby';
import Link from 'gatsby-link';

const IndexPage = (props) => (
  <div>
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <ul>{props.data.allContentfulBlog.edges.map( (data) => (
      <li>
        <Link to={ data.node.slug }>{ data.node.title }</Link>
        <p>{ data.node.content.childMarkdownRemark.excerpt }</p>
      </li>
      ) )}
      <li><Link to="/page-2/">Go to page 2</Link></li>
    </ul>
  </div>
);

export default IndexPage;

export const pageQuery = graphql`
    query pageQuery {
        allContentfulBlog(
            filter: {
                node_locale: {eq: "en-US"}
            },
            sort: {
                fields: [createdAt], order: DESC
            }
        ) {
            edges {
                node {
                    title
                    slug
                    content {
                        childMarkdownRemark {
                            excerpt
                        }
                    }
                }
            }
        }
    }
`

So now when we view the home page of our app we get links to our posts:

Screen Shot 2018-06-22 at 5.00.51 pm

Deploy to Netlify

The first step is to push your repository to GitHub/GitLab/Bitbucket, then head on over to Netlify and sign up for an account. Once you are into the dashboard click New site from Git. Then select your remote git provider and then select your repository. When you get to the last screen, 3. Build options, and deploy!, click show advanced and then click New variable and add the variables from your .env like below:

Screen Shot 2018-06-22 at 5.24.32 pm

From there Netlify's bot's will automatically build your site and the next page will give you the URI. You're now set to add more blogs and build your site. Each time you push changes to your remote git provider, Netlify will update the site. It it also possible to add a build hook for Contentful so Netlify will update the site when a new blog post is published.

Conclusion

I hope you found this useful, if you have any problems you can contact me on Twitter @rhysonrails or click the email link below to send me an email.