What am I misunderstanding in gatsby-node when trying to build a template page for categories or tags?

Viewed 721

Trying to learn Gatsby I'm confused what I'm doing wrong when it comes to building a paginated page for a category. If a post has a category created from the frontmatter of:

---
date: '2017-08-10'
title: 'Sweet Pandas Eating Sweets'
slug: 'sweet-pandas-eating-sweets'
description: 'Something different to post'
category: ['sweet', 'yep', 'two']
tags: ['3', '2']
---

in my gatsby-node.js I dont have any issues (or thrown issues I should say) with single post generation but the console reads:

Failed to load resource: the server responded with a status of 404 (Not Found)

when the category is clicked but not when the <Link> is clicked to go to the single post, the file:

exports.createPages = async ({ actions, graphql }) => {
  const { data } = await graphql(`
    query {
      allMdx(sort: { fields: frontmatter___date, order: DESC }) {
        edges {
          node {
            id
            frontmatter {
              title
              slug
              tags
              category
              date
            }
          }
        }
      }
    }
  `)

  if (data.errors) {
    console.error(data.errors)
    throw data.errors
  }

  // const tagSet = new Set()
  // const categorySet = new Set()

  data.allMdx.edges.map(edge => {
    const slug = edge.node.frontmatter.slug
    const id = edge.node.id
    actions.createPage({
      path: slug,
      component: require.resolve(`./src/templates/singlePost.js`),
      context: { id },
    })
  })

  const postPerPage = 3
  const numPages = Math.ceil(data.allMdx.edges.length / postPerPage)
  Array.from({ length: numPages }).forEach((_, i) => {
    actions.createPage({
      path: i === 0 ? `/` : `/${i + 1}`,
      component: require.resolve(`./src/templates/category.js`),
      context: {
        limit: postPerPage,
        skip: i * postPerPage,
        numPages,
        currentPage: i + 1,
      },
    })
  })
}

and in templates/category.js:

import React from 'react'
import { graphql } from 'gatsby'

// Components
import Layout from '../components/Layout'

const category = ({ location, pageContext, data }) => {
  const { currentPage, numPages } = pageContext
  const isFirst = currentPage === 1
  const isLast = currentPage === numPages
  const prevPage = currentPage - 1 === 1 ? '/' : `/${currentPage - 1}`
  const nextPage = `/${currentPage + 1}`
  const posts = data.allMdx.edges

  console.log('data', data)
  return (
    <>
      <Layout>categories</Layout>
    </>
  )
}

export const pageQuery = graphql`
  query CategoryQuery($id: String!, $skip: Int!, $limit: Int!) {
    allMdx(
      sort: { fields: frontmatter___date, order: DESC }
      skip: $skip
      limit: $limit
      filter: { frontmatter: { category: { eq: $id } } }
    ) {
      edges {
        node {
          id
          frontmatter {
            title
            category
            date
            description
            slug
          }
        }
      }
    }
  }
`

export default category

In GraphiQL both my queries work, example with filtering for just a category:

{
"id":"two"
}

query:

query CategoryQuery($id: String!) {
  allMdx(
    sort: {fields: frontmatter___date, order: DESC}
    filter: {frontmatter: {category: {eq: $id}}}
  ) {
    edges {
      node {
        id
        frontmatter {
          title
          category
          date
          description
          slug
        }
      }
    }
  }
}

Research:

Why when I click on a category from a post if I add it up in the header it goes to a 404 Page and indicates there is no page yet for /category?

Edit:

Stripped down sandbox of what's going on and the issue:

Edit Invisible Backdrop

Tried changing the node from the id to the slug and the result is the same:

  tagsQuery.data.allMdx.edges.forEach(edge => {
    const slug = edge.node.frontmatter.slug
    actions.createPage({
      path: `category/${slug}`,
      component: require.resolve(`./src/templates/Category.js`),
      context: { slug },
    })
  })

from the 404 page, under pages it shows:

/category/undefined
/404/
/
/404.html
/post-slug
/post-slug
2 Answers

There are a few things that may cause issues:

  • Your templates/category component must be capitalized:

      const Category = ({ location, pageContext, data }) => {
      const { currentPage, numPages } = pageContext
      const isFirst = currentPage === 1
      const isLast = currentPage === numPages
      const prevPage = currentPage - 1 === 1 ? '/' : `/${currentPage - 1}`
      const nextPage = `/${currentPage + 1}`
      const posts = data.allMdx.edges
    
      console.log('data', data)
      return (
        <>
          <Layout>categories</Layout>
        </>
      )
    }
    

    Otherwise, React won't be able to infer and create a valid component.

  • Before going into paginating your categories or tags, I would try to create a step-by-step approach, creating successfully the category template. You are re-using the same query to create as well posts and categories but you are never looping to create a category. I would segregate the logic into two separate GraphQL queries:

      exports.createPages = async ({ actions, graphql }) => {
        const postsQuery = await graphql(`
          query {
            allMdx(sort: { fields: frontmatter___date, order: DESC }) {
              edges {
                node {
                  id
                  frontmatter {
                    title
                    slug
                    tags
                    category
                    date
                  }
                }
              }
            }
          }
        `)
    
        const tagsQuery = await graphql(`
          query {
            allMdx(sort: { fields: frontmatter___date, order: DESC }) {
              edges {
                node {
                  id
                  frontmatter {
                    tags
                    category
                  }
                }
              }
            }
          }
        `)
    
    
        if (postsQuery.data.errors || tagsQuery.data.errors) {
          console.error(data.errors)
          throw data.errors
        }
    
        // const tagSet = new Set()
        // const categorySet = new Set()
    
        tagsQuery.data.allMdx.edges.map(edge => {
          const slug = edge.node.frontmatter.slug
          const id = edge.node.id
          actions.createPage({
            path: slug, // or `category/${slug}`
            component: require.resolve(`./src/templates/category.js`),
            context: { id },
          })
        })
    
    
        postsQuery.data.allMdx.edges.map(edge => {
          const slug = edge.node.frontmatter.slug
          const id = edge.node.id
          actions.createPage({
            path: slug,
            component: require.resolve(`./src/templates/singlePost.js`),
            context: { id },
          })
        })
        /*
        const postPerPage = 3
        const numPages = Math.ceil(data.allMdx.edges.length / postPerPage)
        Array.from({ length: numPages }).forEach((_, i) => {
          actions.createPage({
            path: i === 0 ? `/` : `/${i + 1}`,
            component: require.resolve(`./src/templates/category.js`),
            context: {
              limit: postPerPage,
              skip: i * postPerPage,
              numPages,
              currentPage: i + 1,
            },
          })
        })*/
      }
    

    To check the created pages, you can always look for the 404 page in gatsby develop (localhost:8000/someOddString) mode, which by default, prints all the created pages.

After further research and tests in GraphiQL I found distinct in allMDX and I was able to query the frontmatter category or tag:

foo.mdx frontmatter:

---
date: '2017-08-10'
title: 'Sweet Pandas Eating Sweets'
slug: 'sweet-pandas-eating-sweets'
description: 'Something different to post'
category: ['sweet', 'yep', 'two']
---

gatsby-node.js:

exports.createPages = async ({ actions, graphql }) => {
  const catsQuery = await graphql(`
    query {
      allMdx(filter: { fileAbsolutePath: { regex: "/content/" } }) {
        distinct(field: frontmatter___category)
      }
    }
  `)

  catsQuery.data.allMdx.distinct.forEach(category => {
    actions.createPage({
      path: `/category/${category}`,
      component: require.resolve(`./src/templates/category.js`),
      context: { category },
    })
  })
}

Related