'gatsby multiple collection routes for multiple markdown folders

I have two markdown collection routes which I want to apply to two different sets of markdowns separated by subfolders.

My folder structure is as follows

appfolder
  content
    projects
      project1.md
      project2.md
    article
      article1.md
      article2.md
  src
    pages
      projects
        {MarkdownRemark.frontmatter__slug}.js 
      articles
        {MarkdownRemark.frontmatter__slug}.js 

The content of projects/{MarkdownRemark.frontmatter__slug}.js is as follows

import React from 'react';
import Layout from "../../components/Layout";
import Nav from "../../components/Nav";
import PageHeader from "../../components/PageHeader";
import Footer from "../../components/Footer";
import SimpleReactLightbox from 'simple-react-lightbox'
import { graphql } from 'gatsby'
const ProjectPage = ({data}) => {
    const fm = data.markdownRemark.frontmatter;
    const html = data.markdownRemark.html;
    return (
        <SimpleReactLightbox>
        <Layout pageTitle={fm.title}>
            <Nav />
            <PageHeader title={fm.title} />
            <Footer />
        </Layout>
        </SimpleReactLightbox>
    );
};


export const query = graphql`
query($id: String!) {
    markdownRemark(id: { eq: $id },fileAbsolutePath: {regex: "/(projects)/" }) {
      html
      frontmatter {
        slug
        title
        summary
        icon
      }
    }
}
`

export default ProjectPage;

But GraphiQL shows that the pages are generated for all md files. How do I restrict each collection route to respective subfolder.



Solution 1:[1]

Think your issues falls at fileAbsolutePath for the regex.

Change (projects) to just projects

Solution 2:[2]

In that cases, I think the easiest approach is adding a "key" value in your frontmatter to make the filter. For example:

---
key: article
title: Test Title
anotherField: Another field value
---
The body of the markdown

Note: change key: article for key: projects when needed

Then, in your GraphQL query use the filter with something like:

export const query = graphql`
query($id: String!) {
    markdownRemark(id: { eq: $id }, filter: { frontmatter: { key: { eq: "article" },  
          }
        },, ) {
      html
      frontmatter {
        slug
        title
        summary
        icon
      }
    }
}
`

Tweak it as you need and check the availability of the filters in the localhost:8000/___graphql playground.

Solution 3:[3]

I just ran into the same problem. It seems like there are no ready-made solutions for your current organization of src/pages. But after running through the discussions on Gatsby, I found there are some workarounds, though I think it's a bit inconvenient.

Like this comment said:

If your source nodes contain something like category: "article" | "blog", you could generate separate routes with file nested in category directory like /{SourceNode.category}/{SourceNode.slug}.tsx.

If your source node does not contain such a category field, then you can append it to the nodes in onCreateNode hook, or create entirely new nodes for your purposes

In your case, if you can add a category field to the frontmatter in your .md files, you can change your organization of src to

appfolder
  content
    projects
      project1.md
      project2.md
    article
      article1.md
      article2.md
  src
    templates
      project-template.js
      article-template.js
    pages
      {MarkdownRemark.frontmatter__category}
        {MarkdownRemark.frontmatter__slug}.js

Then projects/{MarkdownRemark.frontmatter__slug}.js goes to templates/project-template.js and the article one is similar.

Now in your {MarkdownRemark.frontmatter__slug}.js file, you can forward the data queried by GraphQL to the corresponding template.

// {MarkdownRemark.frontmatter__slug}.js
const GeneratedPage = ({ data }) => {
  const templates = {
    project: <ProjectTemplate data={data} />,
    article: <ArticleTemplate data={data} />,
  };

  return templates[data.MarkdownRemark.frontmatter.category];
};

If it's not possible to add the category field into the .md file, make use of the createNodeField API to programmatically add it to MarkdownRemark nodes in gatsby-node.js. It maybe something like this:

// gatsby-node.js
exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions;
  if (node.internal.type === `MarkdownRemark`) {
    const value = ...; // something mapping the directory of node to certain catecory
    createNodeField({
      name: 'category',
      node,
      value,
    });
  }
};

Hope this can be helpful to you.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 DᴀʀᴛʜVᴀᴅᴇʀ
Solution 2 Ferran Buireu
Solution 3 Kento