Adding custom fields to Markdown Remark nodes
In my case I moving from Jekyl to Gatsby so has bazillion of YYYY-MM-DD-title.md
files.
Moved everything to:
src/
└── notes/
└── YYYY/
└── YYYY-MM-DD-title/
└── README.md
Which allows my to hold some related assets side by side with note itself.
Oh and finally I get rid of frontmatter stuff in markdown files.
Idea here is following:
- folder name, without date will be and url path
- first heading in markdown will be title
So the question now how to tell Gatsby how to build site from this
After few attempts ended up with following:
const assert = require('assert')
const { resolve } = require('path')
exports.createPages = async ({ graphql, actions }) => {
const seen = new Set()
const { data } = await graphql(`
query {
allMarkdownRemark {
nodes {
# id will be passed as a page context
id
fields {
# here is our custom field
# for: src/notes/2021/2021-11-14-hello/README.md
# outputs: hello
path
}
}
}
}
`)
data.allMarkdownRemark.nodes.forEach((node) => {
const path = node?.fields?.path
// just in case, we are not allowing same names
if (seen.has(path)) {
assert.fail(`"${path}" already exists`)
}
seen.add(path)
actions.createPage({
path,
component: resolve('./src/templates/note.js'),
context: {
id: node?.id // pass page id to context
}
})
})
}
// Example of adding custom fields to a node
exports.onCreateNode = ({ node, actions }) => {
const { createNodeField } = actions
// we are dealing only with markdown
if (node.internal.type === 'MarkdownRemark') {
// extract usefull pieces from file absolute path
const match = node.fileAbsolutePath.match(/\/(\d{4})\/(\d{4}-\d{2}-\d{2})-([^/]+)\/README\.md/)
if (match) {
createNodeField({
node,
name: 'year',
value: parseInt(match[1])
})
createNodeField({
node,
name: 'path',
value: match[3]
})
}
}
}
Now inside our template we can perform query like:
query GetNoteById($id: String!) {
markdownRemark(id: { eq: $id }) {
html
fields {
path
year
}
}
}
Or for example on a home page to get last articles:
{
recent: allMarkdownRemark(sort: { fields: fields___year, order: DESC }, limit: 5) {
nodes {
fields {
path
year
}
}
}
}
As you can see, we can not only query fields but also use them for filtering and sorting