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,})})}}
Related
[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' }...]
*/
}
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.
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,
})
})
}
}
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,
},
})
})
}
I'm using next.js and the import { useRouter } from 'next/router'.
However on initial load, router.query.id is undefined. It quickly fills in, but that initial load is a killer.
I'm trying to figure out how to do it, and tried:
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
// const res = await fetch(`https://.../posts/${params.id}`)
// const post = await res.json()
console.log(params)
// Pass post data to the page via props
return { props: { params } }
}
but this returns an error:
Error: getStaticPaths is required for dynamic SSG pages and is missing for '/app/d/[id]'.
I can't use getStaticPaths, since [id] is variable and can be any number of things.
So what's the best way to handle this?
I would do smt like this(without staticProps):
function Page(props) {
const router = useRouter();
const { query = {} } = router || {};
const { id = 0 } = query || {};
useEffect(()=> {
if(id) {
(async ()=> {
const res = await fetch(`https://.../posts/${id}`)
const post = await res.json();
})();
}
}, [id]);
}
And this is what official doc. says:
// You also have to define a Post component in the same file (pages/posts/[id].js)
function Post({ post }) {
const router = useRouter()
// If the page is not yet generated, this will be displayed
// initially until getStaticProps() finishes running
if (router.isFallback) {
return <div>Loading...</div>
}
return <h1>Posts are here</h1>;
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// Pass post data to the page via props
return { props: { post } }
}
UPDATE:
After a bit research, have figure out this solution with staticProps:
export default function Post({ post }) {
return <h1>Post is here</h1>;
}
export async function getStaticPaths() {
return {
paths: [
{ params: { id: '*' } }
],
fallback: true
};
}
export async function getStaticProps(context) {
const res = await fetch(`https://api.icndb.com/jokes/random/${context.params.id}`);
const post = await res.json()
return { props: { post } }
}