Gatsby: Async await with nested graphql queries in gatsby-node

Created on 23 Jul 2019  路  2Comments  路  Source: gatsbyjs/gatsby

What am I doing wrong? I am calling graphql async but createPage is throwing this error. This error started when I introduced the categoryProducts query, I do not believe the initial graphql query is an issue.

warn Action createPage was called outside of its expected asynchronous lifecycle createPages in default-site-plugin.
Ensure that you return a Promise from createPages and are awaiting any asynchronous method
invocations (like graphql or http requests).
For more info and debugging tips: see https://gatsby.dev/sync-actions

gatsby-node.js:

exports.createPages = async function createPages({
    actions: { createPage },
    graphql,
}) {
    const result = await graphql(`
    query {
        site {
            siteMetadata {
                siteUrl
            }
        }
        prisma {
            products {
                globalId
            }
            subCategories {
                slug
                name
                formattedName
            }
            categories {
                slug
                name
                formattedName
            }
        }
      }
    `).then(result => result.data)
    // handle errors
    if (result.errors) throw new Error(result.errors);

    const compareTemplate = path.resolve('src/templates/compare/index.jsx');

    // I do some product page generation here

    result.prisma.categories.forEach(async (node) => {
        const categorySlug = node.slug;

        const categoryProducts = await graphql(`
            query ($categorySlug: String!) {
                prisma {
                    products(
                    orderBy: verdictRating_DESC,
                    where: {
                        categories_some: {slug: $categorySlug },
                    },
                    ) {
                    globalId
                }
            }
        `, { categorySlug: categorySlug }).then(categoryProducts => categoryProducts.data.prisma.products)
        // handle errors
        if (categoryProducts.errors) throw new Error(categoryProducts.errors);

        const productsPerPage = 10;
        const productChunks = chunk(categoryProducts, productsPerPage);
        const numPages = productChunks.length;

        if (productChunks && productChunks.length) {
            productChunks.forEach((products, i) => {

               createPage({
                    component: compareTemplate,
                    path: i === 0 ? `${categorySlug}/` : `${categorySlug}/${i + 1}`,
                    context: {
                        categorySlug,
                        numPages,
                        products,
                        currentPage: i + 1,
                    },
                })
            })
        }
awaiting author response

Most helpful comment

It seems like you have a mistake in your forEach.
result.prisma.categories.forEach(async (node) => {

try to change it into:

await Promise.all(result.prisma.categories.map(async (node) => {

// your code

}))

The foreach is called with async which means the function will be executed on the next tick so asynchronous. If you don't await the promises we will have left the createPages lifecycle.

If this fixes your issue, please close it.

All 2 comments

It seems like you have a mistake in your forEach.
result.prisma.categories.forEach(async (node) => {

try to change it into:

await Promise.all(result.prisma.categories.map(async (node) => {

// your code

}))

The foreach is called with async which means the function will be executed on the next tick so asynchronous. If you don't await the promises we will have left the createPages lifecycle.

If this fixes your issue, please close it.

Perfect. Thank you @wardpeet

Was this page helpful?
0 / 5 - 0 ratings