Gatsby: Index page "/" fails to load when using MDX, other path loads - javascript

I am using Gatsby to create a documentation site, using .mdx to manage contents. I have configured the gatsby-node.js and created the template. Everything path works fine, however, the index page fails to fetch results from the GraphQL query. I have cleared the cache and rebuilt the project severally. I tried a fix on Github which suggested renaming the index.js to main.js. This worked seamlessly on dev but not in prod.
Here's the gatsby-node.js file:
const {createFilePath} = require(`gatsby-source-filesystem`)
const path = require(`path`)
exports.createPages = ({actions, graphql}) => {
const {createPage} = actions
const blogPostTemplate = path.resolve('src/templates/postTemplate.js')
return(graphql(`
{
allMdx {
nodes {
frontmatter {
title
}
fields {
slug
}
}
}
}
`)).then(result => {
if(result.errors) {
throw result.errors
}
const posts = result.data.allMdx.nodes
posts.forEach(post => {
createPage({
path: post.fields.slug,
component: blogPostTemplate,
context: {
slug: post.fields.slug
}
})
})
})
}
exports.onCreateNode = ({node, actions, getNode}) => {
const { createNodeField } = actions
if(node.internal.type === `Mdx`) {
const value = createFilePath({node, getNode})
createNodeField({
name: `slug`,
node,
value
})
}
}
Here's the postTemplate.js file:
import React from 'react'
import {graphql} from 'gatsby'
import {MDXRenderer} from 'gatsby-plugin-mdx'
import Layout from '../components/Layout/Layout';
export default ({data}) => {
const {body, tableOfContents, fields} = data.mdx
return (
<Layout>
{tableOfContents}
<MDXRenderer>
{body}
</MDXRenderer>
{fields}
</Layout>
)
}
export const query = graphql`
query PostBySlug($slug: String!) {
mdx (fields: {slug: {eq: $slug}}) {
body
tableOfContents
fields {
slug
}
}
}
`
Any suggestions would be highly appreciated.

I found a solution to my issue. I discovered the index (/) page wasn't making use of the postTemplate.js. So my workaround was to copy the content of the postTemplate.js file into the index.js file. I then modified the graphql query to fetch just the / content. I don't know if this is the best solution, but it works fine now.

Use a ternary operator for your createPage path. I'm assuming your index's slug name is "index".
path: post.fields.slug === 'index' ? '/' : `/${post.fields.slug}`

Related

Gatsby Head API not working in production build

I am new to Gatsby. I have almost created the entire application sourcing data from my WordPress blog. Everything is working fine except the head API is not outputting any head tag in production. I am using "Add WPGraphQL SEO" plugin to get Yoast SEO data. The query returns an object named fullHead that contains all the page/post's head data.
Here is the post template code -
import React from "react"
import Layout from "../components/layouts/single-post-layout.js"
import { graphql } from "gatsby"
import HTMLReactParser from "html-react-parser"
import SinglePost from "../components/posts/single-post.js"
export const Head = ({ data }) => (
<>{HTMLReactParser(data.wpPost?.seo?.fullHead)}</>
)
const Post = ({ data }) => {
return (
<Layout post={data}>
<SinglePost post={data.wpPost} />
</Layout>
)
}
export const query = graphql`
query($id: String) {
wpPost(id: { eq: $id }) {
id
categories {
nodes {
id
name
uri
}
}
commentCount
content
date
excerpt
featuredImage {
node {
altText
localFile {
childImageSharp {
gatsbyImageData(
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
}
slug
uri
title
tags {
nodes {
id
name
uri
}
}
author {
node {
avatar {
url
}
name
description
uri
}
}
comments {
nodes {
author {
node {
avatar {
url
}
name
url
}
}
id
content
date
}
}
seo {
fullHead
title
breadcrumbs {
url
text
}
}
}
}
`
export default Post
The above code works fine in the development build but not in the production build. What am I doing wrong here?

Unknow field error in Gatsby GraphQL for custom query

I am usingGatsby with GraphQL. Getting unknown field error as follows:
GraphQL Error Encountered 2 error(s):
- Unknown field 'allBlogPost' on type 'Query'. Source: document `homeAymanDesktopFirstSrcPagesIndexJs2258072279` file: `GraphQL request`
GraphQL request:3:3
2 | {
3 | posts: allBlogPost {
| ^
4 | nodes {
- Unknown field 'blogPost' on type 'Query'.
file: /home/ayman/Desktop/first/src/templates/post.js
Here is my post.js template file:
import React from "react"
import { graphql } from "gatsby"
export default ({ data }) => {
console.log(data)
return (
<div>
<h1>{data.post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: data.post.html }} />
</div>
)
}
export const query = graphql`
query($id: String!) {
post: blogPost(id: { eq: $id }) {
title
html
}
}
`
My gatsby-node.js configuration can be found in this pastebin
My GraphiQL:
Update
After adding Asciidoc file in content/posts and content/articles , getting this error:
Cannot query field "slug" on type "BlogPost".
File: gatsby-node.js:89:16
ERROR #11321 PLUGIN
"gatsby-node.js" threw an error while running the createPages lifecycle:
Cannot read property 'posts' of undefined
TypeError: Cannot read property 'posts' of undefined
- gatsby-node.js:99
Your code is breaking in the gatsby-node.js, not in the template (at least for now).
Try using an async/await approach in your createPage API, something like:
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const postTemplate = path.resolve(`./src/templates/post.js`)
const postsQuery = await graphql(`
{
posts: allBlogPost {
nodes {
id
slug
}
}
}
`);
if (postsQuery.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`);
return;
}
postsQuery.data.posts.nodes.forEach( node => {
createPage({
path: node.slug,
component: postTemplate,
context: {
id: node.id,
},
})
})
It seems that you can't create your own schema properly. Ensure that your validations are correct:
if (node.internal.type === `Asciidoc`)

Getting an "Exported queries are only executed for Page components." in Gatsby when trying to generate pages

This seems to be a relatively common problem. I am trying to generate blog post pages but am experiencing this error and the pages show a 404 on load. Which means that they are not being generated.
Here is my code for the gatsby.node.js file:
exports.createPages = async ({ graphql, useStaticQuery, actions: { createPage } }) => {
const postQuery = graphql(`
{
gcms {
posts(where: { stage: PUBLISHED }) {
id
slug
}
}
}
`);
const {
gcms: { posts },
} = useStaticQuery(postQuery);
posts.forEach(({ id, slug }) =>
createPage({
path: `/blog/${slug}`,
component: require.resolve(`./src/templates/PostPage.js`),
context: {
id: id,
slug: slug,
},
})
);
};
And my code for the blog post PostPage.js file:
/* eslint-disable react/prop-types */
import React from 'react';
import { graphql } from 'gatsby';
import Layout from "../components/layout";
//import galaxy from "../images/galaxy.jpg";
import SEO from "../components/seo";
export const postPageQuery = graphql`
query PostPageQuery($id: ID!) {
gcms {
post(where: { id: $id }) {
title
slug
excerpt
postContentMarkdown
tags
author {
name
biography
}
seo {
title
description
keywords
}
}
}
}
`;
const PostPage = ({data: {post}}) => {
return (
<Layout>
<SEO
keywords={[
`ui`,
`ux`,
]}
title="Blog" />
{post.slug}
</Layout>
);
};
export default PostPage;
There are a few things that caught my attention and may fix your issue.
The usage of useStaticQuery in your gatsby-node.js. You don't need to fetch postQuery data with the static query hook since you are using the hook outside a component.
The usage of where filter. According to GraphQL documentation, the way to filter data is by using filter filter. In addition, when filtering the filtered criteria are strings, so must be quoted.
When you pass a field via context API to your PostPage, you should avoid filter to all your gcms since your template has the information of that post, is not needed to redo the same previous query again (same than gatsby-node.js), it's not optimal. I will let it there since I don't know how is your data structured but should be refactored.
Applying it to your code should look like this.
gatsby-node.js:
exports.createPages = async ({ graphql, useStaticQuery, actions: { createPage } }) => {
const postQuery = graphql(`
{
gcms {
posts(filter: { stage: "PUBLISHED" }) {
id
slug
}
}
}
`);
let {posts}= postQuery.gcms;
posts.forEach(({ id, slug }) =>
createPage({
path: `/blog/${slug}`,
component: require.resolve(`./src/templates/PostPage.js`),
context: {
id: id,
slug: slug,
},
})
);
};
PostPage.js:
/* eslint-disable react/prop-types */
import React from 'react';
import { graphql } from 'gatsby';
import Layout from "../components/layout";
//import galaxy from "../images/galaxy.jpg";
import SEO from "../components/seo";
export const postPageQuery = graphql`
query PostPageQuery($id: ID!) {
gcms {
post(filter: { id: $id }) {
title
slug
excerpt
postContentMarkdown
tags
author {
name
biography
}
seo {
title
description
keywords
}
}
}
}
`;
const PostPage = ({data: {post}}) => {
return (
<Layout>
<SEO
keywords={[
`ui`,
`ux`,
]}
title="Blog" />
{post.slug}
</Layout>
);
};
export default PostPage;
I ended up fixing this by doing a complete recomposition of my project, with an update to the latest version of Gatsby, this with a bare bones gatsby starter, plugin by plugin. It ended up being plugin conflict issue. I'm not sure which plugin exactly it was, but most likely it was one of these:
gatsby-plugin-eslint, gatsby-plugin-offline, gatsby-plugin-root-import or possibly the prop-types NPM package.
I experienced this same issue after upgrading to the latest version of Gatsby.
Similarly to Tapha's answer, it was a plugin conflict for me. I had yet to upgrade the gatsby-source-strapi plugin. Upgrading that package to its latest available version solved the issue. Whatever your data source happens to be, I would check that it's still playing nice with Gatsby.
So, this warning/error message is very misleading, you can still use an exported query from templates, as detailed in the Gatsby documentation here.

Resolve Nextjs URL to a selected component

From what I understand Nextjs resolves URLs by mapping them to their respective file in pages folder. So pages/about-us.js will be accessable via href="/about-us".
I want to create multiple languages but don't wanna duplicate the necessary components/JS files. So assuming I have an about-us.js with following content:
<Head title={meta}/>
<Nav/>
<MainContent language={lang}/>
<Footer/>
How can I map /pl/about-us to the /about-us.js in the root of pages without creating another about-us.js in /pages/pl/..?
One of the solution I can think of is to pass the language as a query param
Example
// code for page/about-us.js page
import { withRouter } from 'next/router';
const AboutUs = ({ router }) => {
const { lang } = router.query;
return <div>Welcome to next.js! Language = {lang}</div>;
};
export default withRouter(AboutUs);
so If you got to about-us?lang=pl it will show
Welcome to next.js! Language = pl
Or instead of parsing language inside every page, you can use custom app.js with the code something like this
// custom _app.js
import React from 'react'
import App, { Container } from 'next/app'
export default class MyApp extends App {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
state = {
language: undefined
};
componentDidMount() {
const { router } = this.props;
this.setState({ language: router.query.lang });
}
render () {
const { Component, pageProps } = this.props
return (
<Container>
<Component {...pageProps} language={this.state.langugage} />
</Container>
)
}
}
so every page will have language passed as a param.
Hope this helps.
UPDATE:
to make a custom routing you need to check disabling file-system routing and write some custom server routing

Apollo Client subscriptions must provide schema

I have no idea why it is happening.
I am listening to subscription postAdded which gets published every time I create a post using mutation.
import React, { Component } from 'react'
import { graphql, gql } from 'react-apollo'
class App extends Component {
componentDidMount() {
this.props.subscribeToNewPosts()
}
render() {
return (
<div>
<h1>Hello world</h1>
{console.log(this.props)}
</div>
)
}
}
const Query = gql`
query Posts {
posts {
id
title
content
author {
firstName
lastName
}
}
}
`
const Subscription = gql`
subscription postAdded {
postAdded {
id
title
content
author {
firstName
lastName
}
}
}
`
App = graphql(Query, {
name: 'posts',
props: props => {
return {
subscribeToNewPosts: () => {
return props.posts.subscribeToMore({
document: Subscription,
updateQuery: (prev, { subscriptionData }) => {
if(!subscriptionData.data) {
return prev
}
const newPost = subscriptionData.data.postAdded
console.log(newPost)
return {
...prev,
posts: [...prev.posts, newPost]
}
}
})
},
posts: props.posts
}
}
})(App)
export default App
The error:
First, this is most likely a server implementation issue. The error is likely being throw by graphql-tools that's your graphql end point.
The exact same thing happened to me while following second part of the Full-stack + GraphQL tutorial on the official blog.
While it is not necessarily the same case, what happened to me was that the tutorial exports the schema object with: export { schema } which is equivalent to export { schema: schema } instead of the usual exports as default, e.g. export default schema
In my graphql server endpoint I import with import schema from './src/schema' which was wrong because schema was exported as a sub object and not the default export. Correct import for my case should have been import { schema } from './src/schema'
TL;DR Check your server code, check your export and import statements. The error message is really misleading but it most likely has to do with module import/ export.
It could have been avoided or giving more explicit information if we were compiling using webpack instead of using babel-node like in the tutorial.

Categories

Resources