Gatsby: How to work with gatsby-node / page creation without restarting server?

Created on 14 Sep 2018  路  4Comments  路  Source: gatsbyjs/gatsby

Summary

Relevant information

When working on Gatsby projects, much of the process relies on gatsby-node.js and the creation of pages through createPages.

Gatsby works great once pages are created, and you can make changes to page content and templates -- but if you need to make any changes to the page creation process, Gatsby doesn't recognize changes in gatsby-node.js and doesn't trigger a hot reload.

When making changes to gatsby-node.js, I have to restart the dev server to see changes. Is there a better way?

Environment (if relevant)


  System:
    OS: macOS High Sierra 10.13.4
    CPU: x64 Intel(R) Core(TM) i5-2400S CPU @ 2.50GHz
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 9.8.0 - /usr/local/bin/node
    npm: 6.4.0 - /usr/local/bin/npm
  Browsers:
    Firefox: 61.0.1
  npmPackages:
    gatsby: next => 2.0.0-rc.13
    gatsby-image: next => 2.0.0-rc.1
    gatsby-link: next => 2.0.0-rc.2
    gatsby-mdx: ^0.1.3 => 0.1.3
    gatsby-paginate: 1.0.16 => 1.0.16
    gatsby-plugin-feed: next => 2.0.0-rc.2
    gatsby-plugin-google-analytics: next => 2.0.0-rc.1
    gatsby-plugin-manifest: next => 2.0.2-rc.1
    gatsby-plugin-offline: next => 2.0.0-rc.4
    gatsby-plugin-react-helmet: next => 3.0.0-rc.1
    gatsby-plugin-sharp: next => 2.0.0-rc.3
    gatsby-plugin-sitemap: next => 2.0.0-rc.1
    gatsby-plugin-twitter: next => 2.0.0-rc.2
    gatsby-remark-copy-linked-files: next => 2.0.0-rc.1
    gatsby-remark-images: next => 2.0.1-rc.1
    gatsby-remark-prismjs: next => 3.0.0-rc.2
    gatsby-remark-smartypants: next => 2.0.0-rc.1
    gatsby-source-filesystem: next => 2.0.1-rc.1
    gatsby-transformer-json: next => 2.1.1-rc.2
    gatsby-transformer-react-docgen: next => 2.1.1-rc.5
    gatsby-transformer-remark: next => 2.1.1-rc.1
    gatsby-transformer-sharp: next => 2.1.1-rc.2
  npmGlobalPackages:
    gatsby-cli: 1.1.58

File contents (if changed)

gatsby-config.js:

module.exports = {
  siteMetadata: {
    title: 'Gatsby Documentation Starter',
    sidebar: {
      pages: [
        {
          slug: '/about',
          title: 'about',
        },
      ],
    },
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-mdx`,
      options: {
        defaultLayouts: {
          posts: require.resolve('./src/templates/posts.js'),
          default: require.resolve('./src/templates/page-default.js'),
        },
      },
    },
    `gatsby-plugin-sharp`,
    `gatsby-plugin-twitter`,
    {
      resolve: `gatsby-plugin-feed`,
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `data`,
        path: `${__dirname}/content/json`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `components`,
        path: `../src/components/`,
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-transformer-json`,
    {
      resolve: `gatsby-plugin-sitemap`,
      options: {
        // output: `/some-other-sitemap.xml`,
        // exclude: [`/path/to/page`, `/another/page`],
        query: `
        {
          site {
            siteMetadata {
              siteUrl
            }
          }

          allSitePage {
            edges {
              node {
                path
              }
            }
          }
      }`,
      },
    },
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        trackingId: '#',
        // Puts tracking script in the head instead of the body
        head: false,
        // Setting this parameter is optional
        anonymize: true,
        // Setting this parameter is also optional
        respectDNT: true,
      },
    },
    `gatsby-transformer-react-docgen`,
    `gatsby-plugin-offline`,
  ],
}

package.json:

{
  "name": "gatsby-starter-default",
  "description": "Gatsby default starter",
  "version": "1.0.0",
  "author": "Kyle Mathews <[email protected]>",
  "dependencies": {
    "@mdx-js/mdx": "^0.15.0",
    "@mdx-js/tag": "^0.15.0",
    "gatsby": "next",
    "gatsby-image": "next",
    "gatsby-link": "next",
    "gatsby-mdx": "^0.1.3",
    "gatsby-paginate": "1.0.16",
    "gatsby-plugin-feed": "next",
    "gatsby-plugin-google-analytics": "next",
    "gatsby-plugin-manifest": "next",
    "gatsby-plugin-offline": "next",
    "gatsby-plugin-react-helmet": "next",
    "gatsby-plugin-sharp": "next",
    "gatsby-plugin-sitemap": "next",
    "gatsby-plugin-twitter": "next",
    "gatsby-remark-copy-linked-files": "next",
    "gatsby-remark-images": "next",
    "gatsby-remark-prismjs": "next",
    "gatsby-remark-smartypants": "next",
    "gatsby-source-filesystem": "next",
    "gatsby-transformer-json": "next",
    "gatsby-transformer-react-docgen": "next",
    "gatsby-transformer-remark": "next",
    "gatsby-transformer-sharp": "next",
    "markdown-it": "^8.4.2",
    "mdx-deck": "^1.7.3",
    "prismjs": "^1.15.0",
    "react": "^16.4.2",
    "react-dom": "^16.4.2",
    "react-helmet": "^5.2.0",
    "react-jss": "^8.6.1",
    "react-markdownit": "^2.5.0"
  },
  "keywords": [
    "gatsby"
  ],
  "license": "MIT",
  "scripts": {
    "build": "gatsby build",
    "develop": "gatsby develop",
    "format": "prettier --write '**/*.js'",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "devDependencies": {
    "prettier": "^1.14.2"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/gatsbyjs/gatsby-starter-default"
  }
}

gatsby-node.js:

/**
 * Implement Gatsby's Node APIs in this file.
 *
 * See: https://www.gatsbyjs.org/docs/node-apis/
 */

const path = require('path')
const componentWithMDXScope = require('gatsby-mdx/component-with-mdx-scope')

const queryComponents = (graphql, name) =>
  new Promise((resolve, reject) => {
    resolve(
      graphql(
        `
          {
            componentMetadata(displayName:{ eq: "${name}"}){
              displayName
              description {
                id
              }
              childrenComponentProp {
                id
              }
              childComponentDescription {
                id
              }
              description {
                id
              }
              methods {
                description
                docblock
              }
              props {
                id
              }
              doclets
            }
          }
        `
      ).then(result => result.data.componentMetadata)
    )
  })

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions
  return new Promise((resolve, reject) => {
    resolve(
      graphql(
        `
          {
            allMdx {
              edges {
                node {
                  id
                  frontmatter {
                    name
                    menu
                    title
                  }
                  parent {
                    ... on File {
                      name
                      sourceInstanceName
                    }
                  }
                  code {
                    scope
                  }
                }
              }
            }
          }
        `
      ).then(result => {
        if (result.errors) {
          console.log(result.errors)
          reject(result.errors)
        }
        // Create blog posts pages.
        result.data.allMdx.edges.forEach(async ({ node }) => {

          // Query GraphQL for the component docblock data from react-docgen
          const componentDocs = await queryComponents(
            graphql,
            node.frontmatter.name
          )
          createPage({
            path: `/${node.frontmatter.menu.toLowerCase()}/${node.parent.name.toLowerCase()}`,
            component: componentWithMDXScope(
              path.resolve('./src/templates/posts.js'),
              node.code.scope
            ),
            context: {
              id: node.id,
              name: node.frontmatter.name,
              description: componentDocs.description.id,
              props: componentDocs.description.id,
              childComponentDescription:
                componentDocs.childComponentDescription.id,
            },
          })
        })
      })
    )
  })
}

gatsby-browser.js: N/A
gatsby-ssr.js: N/A

question or discussion

All 4 comments

@pieh @m-allanson @KyleAMathews any future plans of providing hot-reload for gatsby-node.js?

This would be quite complex to do right now, gatsby-node does a lot more things than just create pages, so we would need to know that only createPages was changed, reload module and run it. This could also cause weird issues if someone store state in gatsby-node context - so for now restarting gatsby develop is your best bet :/

@whoisryosuke We'll be closing this issue, as restarting gatsby develop is the best option

Is this problem confined to pages created in gatsby-node.js or does hot reloading not work for any programmatically created pages? In other words, is it possible to do this another way without resorting to restarting gatesby develop (which in our case takes quite a while).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

benstr picture benstr  路  3Comments

3CordGuy picture 3CordGuy  路  3Comments

KyleAMathews picture KyleAMathews  路  3Comments

dustinhorton picture dustinhorton  路  3Comments

jimfilippou picture jimfilippou  路  3Comments