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
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
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
}
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!
}
`)
}
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!
}
`)
}
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()
}
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!
}
`)
}
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)
},
}
},
})
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")
}
`)
}
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)