javascript – Gatsby createPages lifecycle method running before onCreateNode method finishes adding needed node fields

I am working on a Gatsby site that uses gatsby-source-wordpress. In my gatbsy-node.js file, I use the onCreateNote lifecycle method to determine if the node is a certain WordPress custom post type, then I reach out to a separate API to get related information for the post type, use createNodeField to add it as a field, and sometimes also use createRemoteFileNode to add images sourced from the API to a field on the new node.

Now this works great most of the time, but occasionally the createPages lifecycle method runs while the image/node code is still happening (I believe). This means that the image fields don’t exist yet, and the page creation fails. Then after it fails, I see a console message in the log that I set up where it notifies me that the new field has successfully been added to the node.

How can I make sure that all of those nodes are finished and the data is complete, BEFORE the createPages lifecycle runs? It seems when the client uploads a larger image, this is more likely to fail… which makes sense if I’m understanding this correctly. Here is the code from my gatsby-node.js file:

const path = require(`path`);
const slash = require(`slash`);

const fetch = require('node-fetch');
const { createRemoteFileNode } = require(`gatsby-source-filesystem`)


exports.onCreateNode = ({ node, actions, store, cache,createNodeId, }) => {
    const { createNode, createNodeField } = actions;

    function getData(url) {
        return new Promise((resolve, reject) => {
            fetch(url)
                .then((response) => response.json())
                .then((data) => {
                    resolve(data);
                });
        })
    }

    if( node.internal.type === "wordpress__wp_location"){
        const yextID = node.acf.yext_entity_id;
        const yextOrthos = node.acf.location_orthodontists;

        try {

            const getLocation = async () => {
                const data = await fetch("https://api.yext.com/v2/accounts/me/entities?api_key=" + process.env.YEXT_API_KEY + "&v=20191114&filter=%7B%22%24anywhere%22%3A%20%22" + yextID + "%22%7D&entityTypes=healthcareFacility")
                .then(response => response.json());

                // Transform the data into json
                if( data && data.response && data.response.count === 1 ){
                    createNodeField({
                        node,
                        name: `yextLocation`,
                        value: data.response.entities(0)
                    });
                } else {
                    console.log("NO LOCATIONS FOUND");
                }
            };

            function getOrthos(){
                let orthodontists = ();

                yextOrthos.forEach( (ortho, i) => {
                    orthodontists.push(getData("https://api.yext.com/v2/accounts/me/entities?api_key=" + process.env.YEXT_API_KEY + "&v=20191114&filter=%7B%22%24anywhere%22%3A%20%22" + ortho.acf.yext_entity_ortho_id + "%22%7D&entityTypes=healthcareProfessional"));
                });

                Promise.all(orthodontists).then( (orthoData) => {
                    if( orthoData.length ){
                        let finalOrthos = ();

                        orthoData.forEach( (finalOrtho, x) => {
                            finalOrthos.push(finalOrtho.response.entities(0));
                        });

                        createNodeField({
                            node,
                            name: `yextOrthos`,
                            value: finalOrthos
                        });

                    } else {
                        console.log("NO DOCTORS FOUND");
                    }
                });
            }

            getLocation();
            getOrthos();

        } catch (error) {
            console.log(error);
        }
    }

    if( node.internal.type === "wordpress__wp_orthodontist"){
        const yextID = node.acf.yext_entity_ortho_id;
        const wpID = node.wordpress_id;

        try {

            const getTextOrtho = async () => {
                const data = await fetch("https://api.yext.com/v2/accounts/me/entities?api_key=" + process.env.YEXT_API_KEY + "&v=20191114&filter=%7B%22%24anywhere%22%3A%20%22" + yextID + "%22%7D&entityTypes=healthcareProfessional")
                .then(response => response.json());

                // Transform the data into json
                if( data && data.response && data.response.count === 1 ){
                    googleProfilePhoto = data.response.entities(0).googleProfilePhoto.url;
                    createNodeField({
                        node,
                        name: `yextOrthodontist`,
                        value: data.response.entities(0)
                    });

                    if( data.response.entities(0).googleProfilePhoto && data.response.entities(0).googleProfilePhoto.url){
                        createNodeField({
                            node,
                            name: `yextProfilePicture`,
                            value: data.response.entities(0).googleProfilePhoto.url
                        });

                        let fileNode = await createRemoteFileNode({
                            url: data.response.entities(0).googleProfilePhoto.url, // string that points to the URL of the image
                            parentNodeId: node.id, // id of the parent node of the fileNode you are going to create
                            createNode, // helper function in gatsby-node to generate the node
                            createNodeId, // helper function in gatsby-node to generate the node id
                            cache, // Gatsby's cache
                            store, // Gatsby's redux store
                        })
                        // if the file was created, attach the new node to the parent node
                        if (fileNode) {
                            console.log("GOOGLE PROFILE NODE CREATED!")
                            node.featuredImg___NODE = fileNode.id
                        } else {
                            console.log("ERROR! fileNode not Created!");
                        }
                    } else {
                        console.log("NO GOOGLE PROFILE PHOTO FOUND");
                    }
                    
                } else {
                    console.log("NO ORTHODONTISTS FOUND");
                }
            }

            const getWpLocations = async () => {
                const data = await fetch(process.env.GATSBY_WP_BASEURL+ "/wp-json/custom_endpoint/v1/locations_by_orthodontist?orthodontist_id=" + wpID).then(response => response.json());

                if( data ){
                    createNodeField({
                        node,
                        name: `wpLocations`,
                        value: data
                    });
                } else {
                    console.log("NO ORTHODONTISTS FOUND");
                }
            }

            getTextOrtho();
            getWpLocations();

        } catch (error) {
            console.log(error);
        }
    }

}

exports.createPages = async ({ graphql, actions }) => {
    const { createPage } = actions;

    const result = await graphql(`
        {
            locations: allWordpressWpLocation(filter: {status: {eq: "publish"}}) {
                nodes {
                    id
                    path
                    acf {
                        location_orthodontists {
                            acf {
                                yext_entity_ortho_id
                            }
                        }
                        yext_entity_id
                    }
                }
            }
            pages: allWordpressPage(
                filter: {
                    wordpress_id: {nin: (177, 183, 8, 42, 44, 185, 46)}
                    status: {eq: "publish"}
                }) {
                nodes {
                    id
                    wordpress_id
                    path
                }
            }
            orthodontists: allWordpressWpOrthodontist(filter: {status: {eq: "publish"}}) {
                nodes {
                    id
                    path
                }
            }
            posts: allWordpressPost(filter: {status: {eq: "publish"}}) {
                nodes {
                    slug
                    id
                }
            }
        }
    `);

    // Check for any errors
    if (result.errors) {
        throw new Error(result.errors);
    }

    const { locations, pages, orthodontists, posts } = result.data;

    const locationTemplate = path.resolve(`./src/templates/location.js`);
    const pageTemplate = path.resolve(`./src/templates/page.js`);
    const orthoTemplate = path.resolve(`./src/templates/orthodontist.js`);
    const postTemplate = path.resolve(`./src/templates/post.js`);
    const blogTemplate = path.resolve(`./src/templates/blog.js`);

    locations.nodes.forEach(node => {
        let orthodontists = ();

        node.acf.location_orthodontists.forEach(ortho => {
            orthodontists.push(ortho.acf.yext_entity_ortho_id);
        });

        let orthodontistList = orthodontists.join();

        createPage({
            path: `${node.path}`,
            component: slash(locationTemplate),
            context: {
                id: node.id,
                yextId: node.acf.yext_entity_id,
                yextOrthoIds: orthodontists
            },
        });
    });

    pages.nodes.forEach(node => {
        createPage({
            path: `${node.path}`,
            component: slash(pageTemplate),
            context: {
                id: node.id,
            },
        });
    });

    orthodontists.nodes.forEach(node => {
        createPage({
            path: `${node.path}`,
            component: slash(orthoTemplate),
            context: {
                id: node.id,
            },
        });
    });

    posts.nodes.forEach(node => {
        createPage({
            path: `${node.slug}`,
            component: slash(postTemplate),
            context: {
                id: node.id,
            },
        });
    });

    const postsPerPage = 12;
    const numPages = Math.ceil(posts.nodes.length / postsPerPage);
    Array.from({ length: numPages }).forEach((_, i) => {
        createPage({
            path: i === 0 ? `/blog` : `/blog/page/${i + 1}`,
            component: slash(blogTemplate),
            context: {
                limit: postsPerPage,
                skip: i * postsPerPage,
                numPages,
                currentPage: i + 1,
            },
        })
    })
};

Thanks for any information you can provide! I imagine this is probably due to me still learning to use asynchronous behavior in JS, but I just can’t seem to find information on how to make this happen.

Please let me know if I can explain the situation any better!

What are the minimal G Suite privileges needed to take over as Super Admin

I am the Super Admin for my company’s G Suite and would like to set up permissions for my CEO so that she can take over as (or reassign the) Super Admin should something happen to me. We would like to do this by giving her the absolute minimum permissions necessary to do so, so that until she takes that step, no other settings can be accidentally changed by her.

What are the minimal G Suite privileges that a user needs to have to be able to take over as Super Admin, or to assign Super Admin to another user?

Which software is actually needed to update for Adobe Lightroom to support new cameras?

I always thought that “Adobe Camera Raw” is the software that maintains all the camera information and allows also Lightroom to read new camera’s specific RAW files.

However, I have updated the “Adobe Camera Raw and DNG Converter” to Beta 12.4 but it does not seem to update Lightroom with the new cameras in the list. Is this a different software from “Adobe Camera Raw”?

enter image description here

I am having issues finding the right definitions what is what on the adobe websites.

plotting – Is it needed to increase the precision of ContourPlot in this case?

I have this function and I want to see where it is zero.
$$frac{1}{16} left(sinh (pi x) left(64 left(x^2-4right) cosh left(frac{2 pi x}{3}right) cos (y)+left(x^2+4right)^2+256 x sinh left(frac{2 pi x}{3}right) sin (y)right)+left(x^2-12right)^2 sinh left(frac{7 pi x}{3}right)-2 left(x^2+4right)^2 sinh left(frac{5 pi x}{3}right)right)+2 left(x^2-4right) sinh left(frac{pi x}{3}right)$$
I use ContourPlot

f(x_, y_) := 
  2 (-4 + x^2) Sinh((π x)/3) + 
   1/16 (((4 + x^2)^2 + 64 (-4 + x^2) Cos(y) Cosh((2 π x)/3) + 
         256 x Sin(y) Sinh((2 π x)/3)) Sinh(π x) - 
      2 (4 + x^2)^2 Sinh((5 π x)/3) + (-12 + x^2)^2 Sinh((
        7 π x)/3));

ContourPlot(
 f(x, y) == 0, {x, 3.465728, 3.465729}, {y, 1.046786, 1.046795}, 
 PlotPoints -> 500)

and I obtain this plot

enter image description here

Now, my question is that can I trust this plot and conclude that the curves do not cross?

Or, I should increase the precision of the plot? And if so, how can I ask Mathematica to give higher precision for the axis in ContourPlot?