DEV Community

Lennart
Lennart

Posted on • Originally published at lekoarts.de

Filter Future Posts on a Gatsby Blog

You can leverage Gatsby's GraphQL data layer to filter blog posts that are in the future. This TODAY variable allows you to e.g. build your site once a day and automatically have it published. A use case would be a podcast website where the author recorded the episode and already has written the description in advance. This way you also don't have to worry about remembering setting a "draft" variable or to be able to push changes up when you want something published.

If you need a TL;DR: You can visit the final example on Codesandbox (have a look at gatsby-node.js and the filtering in src/pages/index.js).

Setup

Install gatsby-starter-blog with Gatsby's CLI:

gatsby new my-blog-starter https://github.com/gatsbyjs/gatsby-starter-blog
Enter fullscreen mode Exit fullscreen mode

When starting the development server you'll see an overview of all posts on the index page. The end goal is to filter out all future posts from this list.

First, you should change the date on one post inside content/blog to a future date. In the linked Codesandbox the post new-beginnings was changed. You will still see all posts in the overview.

Stop the development server and install a function from Lodash that you'll use in the next step:

npm install lodash.get
Enter fullscreen mode Exit fullscreen mode

Creating the extension

Open your gatsby-node.js file and add the installed package, and the boilerplate to use Gatsby's schema customization API:

const get = require("lodash.get")

exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes, createFieldExtension } = actions
}
Enter fullscreen mode Exit fullscreen mode

With the function createTypes you'll explicitly define the GraphQL types (in this case in GraphQL SDL syntax), with createFieldExtension you'll create a so called directive/extension that you then can reuse throughout your types.

Since gatsby-starter-blog is powered by Markdown you'll want to add your isFuture field to the markdown type. It's called MarkdownRemark — you are probably used to querying that in GraphQL queries. Going to GraphiQL (localhost:8000/___graphql) and Docs (top right corner) => Query also reveals all available types.

Adding this type definition defines the mentioned field at the root of the Markdown type:

const get = require("lodash.get")

exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes, createFieldExtension } = actions

  createTypes(`
    type MarkdownRemark implements Node {
      isFuture: Boolean!
    }
  `)
}
Enter fullscreen mode Exit fullscreen mode

The notation Boolean! means that the field is of type Boolean and is Non-Nullable.

Creating the helper function

In the next step you'll create the helper function that returns either true or false depending on whether the input date is in the future or not.

const get = require("lodash.get")

exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes, createFieldExtension } = actions

  const isFuture = (fieldName) => (source) => {
    const date = get(source, fieldName)
    return new Date(date) > new Date()
  }

  createTypes(`
    type MarkdownRemark implements Node {
      isFuture: Boolean!
    }
  `)
}
Enter fullscreen mode Exit fullscreen mode

The concept used there is called Currying. For this example it means:

If you run isFuture("myName") you'll receive a function:

;(source) => {
  const date = get(source, "myName")
  return new Date(date) > new Date()
}
Enter fullscreen mode Exit fullscreen mode

So what is that function doing? With the lodash.get function you're able to input a string with only one word (e.g. myName) or a word with dot notation (e.g. myName.field). The latter is used to access information in objects. The lodash.get function translates the string and enables you to access that information. If you later console.log(source) you'll see that you have access to source.frontmatter and other fields. It then checks the date with the current date and returns true if it's in the future, otherwise false.

Next, use createFieldExtension and the just created isFuture function:

const get = require("lodash.get")

exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes, createFieldExtension } = actions

  const isFuture = (fieldName) => (source) => {
    const date = get(source, fieldName)
    return new Date(date) > new Date()
  }

  createFieldExtension({
    name: "isFuture",
    args: {
      fieldName: "String!",
    },
    extend({ fieldName }) {
      return {
        resolve: isFuture(fieldName),
      }
    },
  })

  createTypes(`
    type MarkdownRemark implements Node {
      isFuture: Boolean!
    }
  `)
}
Enter fullscreen mode Exit fullscreen mode

createFieldExtension accepts three arguments. name will be name of the directive, args the information you can give it and extend is a function that has to return a (partial) field config. In more verbose terms (including isFuture) you could write it like this:

const isFuture = (fieldName, source) => {
  const date = get(source, fieldName)
  return new Date(date) > new Date()
}

createFieldExtension({
  name: "isFuture",
  args: {
    fieldName: "String!",
  },
  extend(options) {
    return {
      resolve(source) {
        return isFuture(options.fieldName, source)
      },
    }
  },
})
Enter fullscreen mode Exit fullscreen mode

So the the code you'll be using is destructuring the options and passing source via Currying to isFuture.

After adding the field extension to the field, the final code should look like:

const get = require("lodash.get")

exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes, createFieldExtension } = actions

  const isFuture = (fieldName) => (source) => {
    const date = get(source, fieldName)
    return new Date(date) > new Date()
  }

  createFieldExtension({
    name: "isFuture",
    args: {
      fieldName: "String!",
    },
    extend({ fieldName }) {
      return {
        resolve: isFuture(fieldName),
      }
    },
  })

  createTypes(`
    type MarkdownRemark implements Node {
      isFuture: Boolean! @isFuture(fieldName: "frontmatter.date")
    }
  `)
}
Enter fullscreen mode Exit fullscreen mode

As the date is defined in the frontmatter the fieldName is frontmatter.date. If you would place the isFuture field in the frontmatter itself you would use only date for the fieldName.

Start the development server, head to GraphiQL and see that you now can query isFuture on MarkdownRemark nodes!

You now can filter allMarkdownRemark with filter: { isFuture: { eq: false } } to hide posts. See an example on Codesandbox.

Where to go from here

Congrats, you just created a generic field extension that allows you to generate a isFuture field! You can not only use that with Markdown files but with any type in the GraphQL schema.

Lookup the type you want to modify in GraphiQL and add the isFuture field where appropriate. If you're unsure what value you have to pass into fieldName, you can inspect the passed source and see what's available.

Top comments (0)