Banner Image

Fetching Markdown Posts From Your Data Layer

Series:Building A Powerful Blog


Hello and welcome to the fifth post in my series on Building a Powerful Blog!🎉 If you haven't already, I would recommend going back to the first post of the series, Creating a Gatsby Blog Site, and reading through the rest of the series before continuing. So far, you've created and deployed your Gatsby site with Netlify, as well as created your Markdown post and stored it in your data layer. You also have learned the power of Gatsby and GraphQL to create dynamic pages for each blog post file stored in your data layer. Let's now find out how we can utilize GraphQL even further to fetch the data from your Markdown posts.


Retrieving From Your GraphQL Data Layer

To pull in and populate your newly created blog pages with the content from your posts, we'll have to retrieve the data from your GraphQL data layer. There are two main ways we can accomplish this:

  1. Fetch the data within "gatsby-node.js" and pass it to your pages using the context field
  2. Fetch the data directly within the "blog-post.js" template file

Both of these files were created in the prior post, Creating Pages from Markdown with GraphQL and both options are a valid approach. I prefer to utilize the second option though as it helps encapsulate the blog post data within each individual page, and keeps your code clean and organized. So let's get started by firing up your server with npm run develop and navigating to http://localhost:8000/___graphql in your browser to open GraphiQL. Here, we're going to play around with the query a bit to identify what we would like to pull in as a starter for our template file.

Right away, I know we're going to want to pull in all of the data from the YAML frontmatter so we should expand the markdownRemark field and the blue frontmatter field within that. Remember, we want the blue field as that specifies the data to be retrieved. The purple fields specify what to filter your data on. From here, click each checkbox that's available. When clicking the checkbox for dateCreated and dateEdited, more will appear under those fields. Be sure to click the formatString checkbox for both and enter "MMMM DD, YYYY". This will format the date fields for you automatically so that you don't have to in your code. There are several format strings that can be entered so feel free to play around until the format appears to your liking.

Next, let's get your banner image in the query as well. From the frontmatter dropdown, click the banner dropdown, then the gatsbyImageData checkbox. This will again display some additional fields where we'll select layout and change its value to FULL_WIDTH, as well as placeholder and change its value to BLURRED. There are many options within the gatsbyImageData field, too many to go into detail here on. If you're interested in learning what each of them are for though, I would recommend reading the Gatsby Image API documentation to utilize it to it's full power.

With the frontmatter taken care of, we can navigate back to the markdownRemark dropdown and select the html and timeToRead fields near the bottom of the dropdown. If you happened to collapse a dropdown in the explorer, you'll notice that the applicable fields within that dropdown all get removed from your query as well. If this happens, just be sure to expand the dropdown again and all of your prior selections will reappear.

Before we can run this query, we need to add in the path it has to search for within your Markdown file. To do this, we'll add ($path: String!) immediately after MyQuery at the top of your query. This tells the query that it should expect a variable of type String and pass it through. We'll then add a filter to only return results where the path field equals the variable being passed through. This is accomplished by adding (frontmatter: {path: {eq: $path}}) immediately after the markdownRemark field of your query. Putting it all together, your query should look similar to the following:

query MyQuery($path: String!) {
  markdownRemark(frontmatter: {path: {eq: $path}}) {
    frontmatter {
      dateCreated(formatString: "MMMM DD, YYYY")
      dateEdited(formatString: "MMMM DD, YYYY")
      path
      title
      description
      banner {
        childImageSharp {
          gatsbyImageData(layout: FULL_WIDTH, placeholder: BLURRED)
        }
      }
    }
    timeToRead
    html
  }
}

To specify a variable for your query to use, we'll have to expand the Query Variables section of GraphiQL found at the bottom of the center panel. Click on this section and add { "path": "/my-first-blog-post" } to it.

Screenshot of the GraphQL query and variables for your blog post query

Once you're ready, you can execute the query using the "play" button at the top of the center panel and view your response in the right panel. If any errors are returned, be sure to read through them fully to understand the issue. It may be that there's a typo in your variable, the formatString options you created for your date fields, or even with how your passing the variable into your query. The error messages from GraphiQL are pretty descriptive to help you debug the issue.

Now that you have a query returning the data you want for your blog post, we can go ahead and transfer that query into your "blog-post.js" file. Navigate to the file within the templates folder of your codebase and import GraphQL and PropTypes at the top of your file.

import PropTypes from "prop-types"
import { graphql } from "gatsby"

PropTypes will help you document the intended types of properties being passed to your function and warn you, in development, if they don't match. This will help you ensure you're always getting back the intended data and assist you in debugging if something goes wrong. There are many different validators within the PropTypes object but we'll only need to know a few, such as string and object.

If you're interested in reading more on the different types of validators, I would recommend checking out the prop-types npm package itself. For now, just know that we'll be adding validators onto the BlogPost object itself and modeling the GraphQL query based off what you created earlier. You'll add the following code snippet immediately before you export the BlogPost object.

BlogPost.propTypes = {
  data: PropTypes.shape({
    markdownRemark: PropTypes.shape({
      frontmatter: PropTypes.shape({
        dateCreated: PropTypes.string.isRequired,
        dateEdited: PropTypes.string.isRequired,
        path: PropTypes.string.isRequired,
        title: PropTypes.string.isRequired,
        description: PropTypes.string.isRequired,
        banner: PropTypes.object,
      }).isRequired,
      timeToRead: PropTypes.number.isRequired,
      html: PropTypes.string.isRequired,
    }).isRequired,
  }).isRequired,
}

export default BlogPost

Here you can see that we're specifying they types of each field, as well as whether they're required or not. Next, we can copy and paste your query into a pageQuery variable, which we'll export so that your BlogPost object can find it. This will get added immediately after you export the BlogPost object and will look similar to the following:

export default BlogPost
  
export const pageQuery = graphql`
  query BlogPostByPath($path: String!) {
    markdownRemark(frontmatter: {path: {eq: $path}}) {
      frontmatter {
        dateCreated(formatString: "MMMM DD, YYYY")
        dateEdited(formatString: "MMMM DD, YYYY")
        path
        title
        description
        banner {
          childImageSharp {
            gatsbyImageData(
              layout: FULL_WIDTH,
              placeholder: BLURRED
            )
          }
        }
      }
      timeToRead
      html
    }
  }
`

You'll notice that I modified the query name from MyQuery to BlogPostByPath. This is mainly done to ensure each query has a unique name and helps you keep things organized.

With your page now retrieving the necessary information from your data layer, let's go ahead and add it as a parameter to your BlogPost function by adding { data } to the parentheses of your arrow function. While we're at it, we'll also deconstruct this data object and assign the markdownRemark object to a variable with const { markdownRemark: post } = data. This will pull the data from that object and assign it to a variable named post to help with readability of your code. From here, we can quickly validate the data being returned from your query by logging the post object to the console. You're BlogPost function should look similar to the following:

const BlogPost = ({ data }) => {
  const { markdownRemark: post } = data
  console.log(post);

  return (
    <h1>Hello World!</h1>
  )
}

Returning to your blog post webpage and opening the console of your DevTools by clicking F12, you should be able to see an object has been logged. When you expand the object, it should look something like: Screenshot of the blog-post object logged to the console of your DevTools

Looking deeper into the object, you can see the various fields that we'll be using to populate our blog post page with. That discussion will have to be left for another day though as this post is already getting pretty lengthy. In the meantime though, I would recommend reading more on the Gatsby Image API to better understand all that can be done to transform and deliver your beautiful images to your readers.


In the next post, we'll further develop the template file to utilize the content retrieved from your data layer. Afterwards, you'll have a clean and minimalistic design for your viewers to read your inspiring blog posts! I hope you enjoyed this post and found it's content helpful as you continue your journey through web development! The links to my social media accounts can be found on the contact page of my website. Please feel free to share any of your own experiences on working with the Gatsby Image API, general questions and comments, or even other topics you would like me to write about. Thank you and I look forward to hearing from you!👋