GatsbyJS: createPage skip pages in WordPress reading settings - reactjs

I am using GatsbyJS & gatsby-source-wordpress on my site. Is it somehow possible to skip both front page and blog page that has been assigned in reading settings on my WordPress site during the creation of pages?
During research I found a WordPress plugin wp-graphql-homepage by Ash Hitchcock which returns a Page type of the homepage and page for post page set under the reading settings. However, still not quite sure if that should be used to accomplish what I am trying to?
I also found I can query for isPostsPage and isFrontPage. Maybe once I have those, I can chain filter onto allWpPage.nodes when about to run createPages.
e.g., allWpPage.nodes.filter(node => !node.isFrontPage || !node.isPostsPage).map(... ? Just not quite sure how?
The reason why I want to skip front page and post page is because I do have a index.js and blog.js placed in src/pages. Because of this, I do get a error during page creation when running gatsby develop:
{ allWpPage: { nodes: [ [Object], [Object], [Object], [Object] ] } }
unexpected error while SSRing the path: /
TypeError: Cannot read property 'localFile' of null
at IndexPage (C:\Users\mig\Desktop\Gatsby\mf20\public\webpack:\lib\src\pages\index.js:164:62)
at processChild (C:\Users\mig\Desktop\Gatsby\mf20\node_modules\react-dom\cjs\react-dom-server.node.development.js:3043:14)
at resolve (C:\Users\mig\Desktop\Gatsby\mf20\node_modules\react-dom\cjs\react-dom-server.node.development.js:2960:5)
at ReactDOMServerRenderer.render (C:\Users\mig\Desktop\Gatsby\mf20\node_modules\react-dom\cjs\react-dom-server.node.development.js:3435:22)
at ReactDOMServerRenderer.read (C:\Users\mig\Desktop\Gatsby\mf20\node_modules\react-dom\cjs\react-dom-server.node.development.js:3373:29)
at renderToString (C:\Users\mig\Desktop\Gatsby\mf20\node_modules\react-dom\cjs\react-dom-server.node.development.js:3988:27)
at generateBodyHTML (C:\Users\mig\Desktop\Gatsby\mf20\public\webpack:\lib\.cache\ssr-develop-static-entry.js:259:34)
at Module../.cache/ssr-develop-static-entry.js.__webpack_exports__.default (C:\Users\mig\Desktop\Gatsby\mf20\public\webpack:\lib\.cache\ssr-develop-static-entry.js:289:19)
at C:\Users\mig\Desktop\Gatsby\mf20\node_modules\gatsby\src\utils\dev-ssr\render-dev-html-child.js:138:9
warn The path "/" errored during SSR.
Edit its component undefined to resolve the error.
My gatsby-node file:
exports.createPages = async ({ actions, graphql, reporter }) => {
const result = await graphql(`
{
allWpPage {
nodes {
__typename
id
databaseId
uri
}
}
}
`)
if (result.errors) {
reporter.error("There was an error fetching pages", result.errors)
}
const { allWpPage } = result.data
const pageTemplate = require.resolve(`./src/templates/page/WpPage.js`)
if (allWpPage.nodes.length) {
allWpPage.nodes.map(page => {
actions.createPage({
path: page.uri,
component: pageTemplate,
context: page,
})
})
}
const result2 = await graphql(`
{
allWpPost {
nodes {
__typename
id
databaseId
uri
}
}
}
`)
if (result2.errors) {
reporter.error("There was an error fetching posts", result.errors)
}
const { allWpPost } = result2.data
const postTemplate = require.resolve(`./src/templates/post/WpPost.js`)
if (allWpPost.nodes.length) {
allWpPost.nodes.map(post => {
actions.createPage({
path: post.uri,
component: postTemplate,
context: post,
})
})
}
}

Almost had it! I had to use isPostsPage and isFrontPage as written in OP. I could then make it skip frontpage and postpage by using eg !node.isfrontPage. My final gatsby-node which works and solves the issue:
exports.createPages = async ({ actions, graphql, reporter }) => {
const result = await graphql(`
{
allWpPage {
nodes {
__typename
id
databaseId
uri
isFrontPage
isPostsPage
}
}
}
`)
if (result.errors) {
reporter.error("There was an error fetching pages", result.errors)
}
const { allWpPage } = result.data
const pageTemplate = require.resolve(`./src/templates/page/WpPage.js`)
if (allWpPage.nodes.length) {
allWpPage.nodes
.filter(node => !node.isFrontPage || !node.isPostsPage)
.map(page => {
actions.createPage({
path: page.uri,
component: pageTemplate,
context: page,
})
})
}
const result2 = await graphql(`
{
allWpPost {
nodes {
__typename
id
databaseId
uri
}
}
}
`)
if (result2.errors) {
reporter.error("There was an error fetching posts", result.errors)
}
const { allWpPost } = result2.data
const postTemplate = require.resolve(`./src/templates/post/WpPost.js`)
if (allWpPost.nodes.length) {
allWpPost.nodes.map(post => {
actions.createPage({
path: post.uri,
component: postTemplate,
context: post,
})
})
}
}
Edit solution two - Probably better solution:
It is possible to filter for isFrontPage and isPostsPage directly using graphql query filters. Something like this:
query MyQuery {
allWpPage(filter: {isFrontPage: {eq: false}, isPostsPage: {eq: false}}) {
nodes {
isPostsPage
isFrontPage
uri
title
}
}
}
Updated and final gatsby-node file:
exports.createPages = async ({ actions, graphql, reporter }) => {
const result = await graphql(`
{
allWpPage(
filter: { isFrontPage: { eq: false }, isPostsPage: { eq: false } }
) {
nodes {
__typename
id
databaseId
uri
}
}
}
`)
if (result.errors) {
reporter.error("There was an error fetching pages", result.errors)
}
const { allWpPage } = result.data
const pageTemplate = require.resolve(`./src/templates/page/WpPage.js`)
if (allWpPage.nodes.length) {
allWpPage.nodes.map(page => {
actions.createPage({
path: page.uri,
component: pageTemplate,
context: page,
})
})
}
const result2 = await graphql(`
{
allWpPost {
nodes {
__typename
id
databaseId
uri
}
}
}
`)
if (result2.errors) {
reporter.error("There was an error fetching posts", result.errors)
}
const { allWpPost } = result2.data
const postTemplate = require.resolve(`./src/templates/post/WpPost.js`)
if (allWpPost.nodes.length) {
allWpPost.nodes.map(post => {
actions.createPage({
path: post.uri,
component: postTemplate,
context: post,
})
})
}
}

Related

NextJS 13 "A required parameter was not provided as a string in generateStaticParams"

[I am upgrading my current NextJS site to the latest version of NextJS 13 with the app directory and using generateStaticParams, I keep getting the following error when migrating my getStaticPaths to the new app directory page, I am using GraphQL:
Error: A required parameter (slug) was not provided as a string in generateStaticParams for /[slug]
I've consoled the returns and it seems to be returning the correct object with strings, not sure what I am missing.
// app/[slug]/page.tsx
export async function generateStaticParams() {
const { data } = await client.query({
query: gql`
query getSlugs {
posts(first: 100) {
nodes {
slug
}
}
}
`,
})
const response = data.posts.nodes.map((post: Post) => post.slug)
const slugs = await response.map((slug: string) => ({ params: { slug } }))
return [{ slugs }]
/* returns
[{ params: { slug: 'blog-post-1' } }, { params: { slug: 'blog-post-2' } }...]
*/
}
async function getPost(params) {
const { data } = await client.query({
query: gql`
query singlePost {
post(id: "${params.slug}", idType: SLUG) {
content
categories {
nodes {
name
}
}
featuredImage {
node {
sourceUrl
}
}
postId
}
}
`,
})
return {data.post}
}
export default async function PostPage({ params }) {
const post = await getPost(params)
return <Post post={post} />
}
You must remove the params key inside the object you return, you must only return the slug.
export async function generateStaticParams() {
const { data } = await client.query({
query: gql`
query getSlugs {
posts(first: 100) {
nodes {
slug
}
}
}
`,
})
const response = data.posts.nodes.map((post: Post) => post.slug)
const slugs = response.map((slug: string) => ({ slug }))
return slugs
/* returns
[{ slug: 'blog-post-1' }, { slug: 'blog-post-2' }...]
*/
}

Gatsby giving error on generating pages from markdown files

I am trying to generate pages from two categories of .md files [projects/posts] , I have combined the query related to both folders in a query as below in gatsby-node.js :
exports.createPage = ({ graphql, actions }) => {
const { createPage } = actions
const blogTemplate = path.resolve("./src/templates/blog-details.js")
const projectTemplate = path.resolve("./src/templates/project-details.js")
return graphql(`
query {
projects: allMarkdownRemark(
filter: { fileAbsolutePath: { regex: "/projects/" } }
sort: { fields: [frontmatter___date], order: DESC }
) {
nodes {
frontmatter {
slug
title
}
fileAbsolutePath
}
}
posts: allMarkdownRemark(
filter: { fileAbsolutePath: { regex: "/posts/" } }
sort: { fields: [frontmatter___date], order: DESC }
) {
nodes {
frontmatter {
slug
title
}
fileAbsolutePath
}
}
}
`).then(result => {
if (result.errors) {
Promise.reject(result.errors)
}
console.log(result)
const projects = result.data.projects.nodes
const posts = result.data.posts.nodes
console.log("starting projects page creation")
console.log(projects)
projects.forEach((node, index) => {
createPage({
path: "/projects/" + node.frontmatter.slug,
component: projectTemplate,
context: { slug: node.frontmatter.slug },
})
})
console.log("starting posts page creation")
console.log(posts)
posts.forEach((node, index) => {
const next = index === 0 ? null : posts[index - 1]
const previous = index === nodes.length - 1 ? null : posts[index + 1]
createPage({
path: "/blog/" + node.frontmatter.slug,
component: blogTemplate,
context: {
slug: node.frontmatter.slug,
previous,
next,
},
})
})
})
}
The query is fetching response , have verified from GraphiQL :
But gatsby develop gives error in creating pages:
Exported queries are only executed for Page components. It's possible you're
trying to create pages in your gatsby-node.js and that's failing for some
reason.
Not able to find the exact reason of the error.
Kindly let me know what I am missing here, thanks.
createPages is an asynchronous action. In addition, you have a typo (notice the trailing "s").
In list of gatsby-node.js APIs you have an onCreatePage function or createPages, but not createPage.
Your createPages should look like:
const path = require("path")
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const queryResults = await graphql(`
query AllProducts {
allProducts {
nodes {
id
name
price
description
}
}
}
`)
const productTemplate = path.resolve(`src/templates/product.js`)
queryResults.data.allProducts.nodes.forEach(node => {
createPage({
path: `/products/${node.id}`,
component: productTemplate,
context: {
// This time the entire product is passed down as context
product: node,
},
})
})
}
As I said, notice the async.

Preparing requested page Gatsby

I'm using Gatsby as a front-end, and Strapi as my back-end to create my website. However, when I clicked my blog, it only shows white blank page, and when I checked it on my localhost, it shows these errors. Anyone have ever experienced this error?
Here is my Gatsby-node.js
const path = require("path")
// create pages dynamically
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
{
blogs: allStrapiBlogs {
nodes {
slug
}
}
}
`)
const prodres = await graphql(`
{
products: allStrapiProduct {
nodes {
slug
}
}
}
`)
result.data.blogs.nodes.forEach(blog => {
createPage({
path: `/blogs/${blog.slug}`,
component: path.resolve(`src/templates/blog-template.js`),
context: {
slug: blog.slug,
},
})
})
prodres.data.products.nodes.forEach(product => {
createPage({
path: `/product/${product.slug}`,
component: path.resolve(`src/templates/product-template.js`),
context: {
slug: product.slug,
},
})
})
}

GatsbyJS: Create Posts & Pages dynamically

I am trying to create posts & pages programmatically using GatsbyJS createPages API. In order to do this am I using gatsby-source-wordpress and the WPGraphQL plugin. All this is done from gatsby-node.
I have tried editing the names of my templates and creating other template paths etc. but nothing seems to work. Any clues on what I am missing?
My code in gatsby-node:
//Create Posts
exports.createPages = async ({ actions, graphql, reporter }) => {
const result = await graphql(`
{
allWpPost {
nodes {
__typename
id
databaseId
uri
}
}
}
`)
if (result.errors) {
reporter.error("There was an error fetching posts", result.errors)
}
const { allWpPost } = result.data
const template = require.resolve(`./src/templates/WpPost.js`)
if (allWpPost.nodes.length) {
allWpPost.nodes.map(post => {
actions.createPage({
path: post.uri,
component: template,
context: post,
})
})
}
}
//Create Pages
exports.createPages = async ({ actions, graphql, reporter }) => {
const result = await graphql(`
{
allWpPage {
nodes {
__typename
id
databaseId
uri
}
}
}
`)
if (result.errors) {
reporter.error("There was an error fetching pages", result.errors)
}
const { allWpPage } = result.data
const template = require.resolve(`./src/templates/WpPage.js`)
if (allWpPage.nodes.length) {
allWpPage.nodes.map(page => {
actions.createPage({
path: page.uri,
component: template,
context: page,
})
})
}
}
My pages are being created succesfully, but my post creation doesn't seem to work at all. I see an error in my terminal:
The GraphQL query in the non-page component
"C:/Users/mig/Desktop/Gatsby/gsite/src/templates/WpPost.js" will not be run.
Exported queries are only executed for Page components. It's possible you're
trying to create pages in your gatsby-node.js and that's failing for some
reason.
If the failing component(s) is a regular component and not intended to be a page
component, you generally want to use a <StaticQuery> (https://gatsbyjs.org/docs/static-query)
instead of exporting a page query.
If you're more experienced with GraphQL, you can also export GraphQL
fragments from components and compose the fragments in the Page component
query and pass data down into the child component — https://graphql.org/learn/queries/#fragments
as mentioned by #natac in comments I had to create pages and post in the same exports.createPages function.
Updated Gatsby-node that solved issue:
exports.createPages = async ({ actions, graphql, reporter }) => {
const result = await graphql(`
{
allWpPage {
nodes {
__typename
id
databaseId
uri
}
}
}
`)
if (result.errors) {
reporter.error("There was an error fetching pages", result.errors)
}
const { allWpPage } = result.data
const pageTemplate = require.resolve(`./src/templates/WpPage.js`)
if (allWpPage.nodes.length) {
allWpPage.nodes.map(page => {
actions.createPage({
path: page.uri,
component: pageTemplate,
context: page,
})
})
}
const result2 = await graphql(`
{
allWpPost {
nodes {
__typename
id
databaseId
uri
}
}
}
`)
if (result2.errors) {
reporter.error("There was an error fetching posts", result.errors)
}
const { allWpPost } = result2.data
const postTemplate = require.resolve(`./src/templates/WpPost.js`)
if (allWpPost.nodes.length) {
allWpPost.nodes.map(post => {
actions.createPage({
path: post.uri,
component: postTemplate,
context: post,
})
})
}
}
It must work:
exports.createPages = async ({ actions, graphql, reporter }) => {
const result = await graphql(`
{allWpPage {
nodes {__typename id databaseId uri}}}`)
if (result.errors) {
reporter.error("There was an error fetching pages", result.errors)
}
const { allWpPage } = result.data
const pageTemplate = require.resolve(`./src/templates/WpPage.js`)
if (allWpPage.nodes.length) {allWpPage.nodes.map(page => {actions.createPage({path: page.uri,component: pageTemplate,context: page,})})}
const result2 = await graphql(`{allWpPost {nodes {__typename id databaseId uri}}}`)
if (result2.errors) {
reporter.error("There was an error fetching posts", result.errors)
}
const { allWpPost } = result2.data
const postTemplate = require.resolve(`./src/templates/WpPost.js`)
if (allWpPost.nodes.length) {allWpPost.nodes.map(post => {actions.createPage({path: post.uri,component: postTemplate, context: post,})})}}

Multiple templates in Gatsby.js

I'm pretty new to Gatsby and want to have multiple templates that i can use for my different pages, eg Landingpage, Contact and About cant have the same templates.
I have made a ugly code in gatsby-node.js for now but it only works for two templates, here is my code (btw I'm sourcing data from markdownfiles):
Mardownfile 1:
---
title: 'Index page'
slug: '/'
template: indexTemplate //This is where i declare which template it shall use
content: 'Index content'
---
Mardownfile 2:
---
slug: '/my-first-post'
date: '2019-05-04'
title: 'My first blog post!'
content: 'Content post 1'
template: postTemplate
---
gatsby-node.js:
exports.createPages = async ({ actions, graphql, reporter }) => {
const { createPage } = actions
const result = await graphql(`
query{
allMarkdownRemark {
nodes {
frontmatter {
slug
template
}
}
}
}
`)
const postTemplate = require.resolve(`./src/templates/postTemplate.js`)
const indexTemplate = require.resolve(`./src/templates/indexTemplate.js`)
const aboutTemplate = require.resolve(`./src/templates/aboutTemplate.js`) // I also want a template for my aboutpage
// Handle errors
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
result.data.allMarkdownRemark.nodes.forEach( post => {
createPage({
path: post.frontmatter.slug,
component: post.frontmatter.template === 'indexTemplate' ? indexTemplate : postTemplate, // Here is where the ugly "magic" happens...
context: {
slug: post.frontmatter.slug,
},
})
})
}
The idea of using gatsby-node.js is to create dynamic pages from some data source (CMS, markdown files, JSON files, external APIs, etc) with the createPage API. In other words, it's really useful to create dynamic pages that have an unknown slug or name from a data source. The idea is to query all types of data (posts for instance) and pass via context some unique information (normally the slug or the id) as a filter parameter to perform another query in your template.
In your case, that fits for your blog (markdown file 2), but not for other known pages such as index, about, etc (markdown file 1). They are defined and known pages and should be treated differently, using a page query. They are not templates because are unique pages, you won't have two different homes that can reuse a home template.
To identify different markdown pages, between posts and home page, for example, you can use a key value to filter your queries.
---
title: 'Index page'
slug: '/'
key: 'home'
template: indexTemplate //This is where i declare which component it shall use
content: 'Index content'
---
Content of the index page.
And:
---
slug: '/my-first-post'
date: '2019-05-04'
key: 'post'
title: 'My first blog post!'
content: 'Content post 1'
template: postTemplate
---
When creating a pages dyamically (via gatsby-node.js) the approach should look like:
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode, basePath: `pages` })
createNodeField({
node,
name: `slug`,
value: slug,
})
}
}
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allMarkdownRemark(
filter: { frontmatter: { key: { eq: "article" }}}) {
edges {
node {
fields {
slug
}
}
}
}
}
`)
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
// Data passed to context is available
// in page queries as GraphQL variables.
slug: node.fields.slug,
},
})
})
}
Note the filter for the key value.
Then, in your blog-post (template):
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
export default function BlogPost({ data }) {
const post = data.markdownRemark
return (
<Layout>
<div>
<h1>{post.frontmatter.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</div>
</Layout>
)
}
export const query = graphql`
query($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
html
frontmatter {
title
}
}
}
`
For your static pages, just make the query on the page. For your index.js:
import React from 'react'
import { graphql } from 'gatsby'
const HomePage = ({data}) => {
return (
<div>
Your page title is {data.nodes[0].frontmatter.title}
</div>
)
}
export const query = graphql`
query HomePageQuery {
allMarkdownRemark(filter: { frontmatter: { key: { eq: "home" }}}) {
nodes{
frontmatter{
title
}
}
}
}
`
export default HomePage
Feel free to adapt the code to your needs. The important thing is that you should differentiate between what should be used as a template (blog, posts, articles, or other dynamic data) and what should be handled statically (home, about, or other static pages).
Of course, upon the scope of the question, you can have multiple templates, let's say one for posts and the other for reviews (or other dynamic data). You can simply achieve it by saving your query result inside a variable and call twice the createPage API:
const result1 = await graphql(`
query{
allMarkdownRemark {
nodes {
frontmatter {
slug
template
}
}
}
}
`)
const result2 = await graphql(`
query{
allMarkdownRemark {
nodes {
frontmatter {
slug
template
}
}
}
}
`)
const postTemplate = require.resolve(`./src/templates/postTemplate.js`)
const reviewsTemplate = require.resolve(`./src/templates/reviewsTemplate.js`)
// Handle errors
if (result1.errors || result2.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
result1.data.allMarkdownRemark.nodes.forEach( post => {
createPage({
path: post.frontmatter.slug,
component: postTemplate
context: {
slug: post.frontmatter.slug,
},
})
})
result2.data.allMarkdownRemark.nodes.forEach( review => {
createPage({
path: review.frontmatter.slug,
component: reviewsTemplate
context: {
slug: review.frontmatter.slug,
},
})
})

Resources