Bart
Bart

Reputation: 339

Error: `siteUrl` does not exist on `siteMetadata` in the data returned from the query. How to resolve?

When building my Gatsby website I get the below error. I've tried to delete and reinstall npm, update the plugins, deleting (Gatsby) cache, playing around with the siteUrl and all kinds of settings in the Gatsby config. But I can't seem to get rid of the error. The development environment works fine.

github: https://github.com/bartfluitsma/gatsby-bart-fluitsma

**Error console log**

ERROR #11321  PLUGIN

"gatsby-plugin-sitemap" threw an error while running the onPostBuild lifecycle:

`siteUrl` does not exist on `siteMetadata` in the data returned from the query.
Add this to your `siteMetadata` object inside gatsby-config.js or add this to your custom query or provide a custom `resolveSiteUrl` function.
https://www.gatsbyjs.com/plugins/gatsby-plugin-sitemap/#api-reference


  47 |             errors = _yield$graphql.errors;
  48 |             _context.next = 9;
> 49 |             return Promise.resolve(resolveSiteUrl(queryRecords)).catch(function (err) {
     |                                    ^
  50 |               return reporter.panic(_internals.REPORTER_PREFIX + " Error resolving Site URL", err);
  51 |             });
  52 |

File: node_modules\gatsby-plugin-sitemap\gatsby-node.js:49:36



  Error: `siteUrl` does not exist on `siteMetadata` in the data returned from the query.
  Add this to your `siteMetadata` object inside gatsby-config.js or add this to your custom query or provide a custom `resolveSiteUrl` function.
  https://www.gatsbyjs.com/plugins/gatsby-plugin-sitemap/#api-reference

  - internals.js:62 resolveSiteUrl
    [gatsby-bart-fluitsma]/[gatsby-plugin-sitemap]/internals.js:62:11

  - gatsby-node.js:49 _callee$
    [gatsby-bart-fluitsma]/[gatsby-plugin-sitemap]/gatsby-node.js:49:36


not finished onPostBuild - 0.480s

Gatsby-config.js

module.exports = {
  siteMetadata: {
    title: `Bart Fluitsma`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
    siteUrl: `http://bartfluitsma.com`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-image`,
    'gatsby-plugin-postcss',
    {
      resolve: `gatsby-plugin-google-fonts-with-attributes`,
      options: {
        fonts: [
          `montserrat\:300,400,400i,600,900`,
        ],
        display: 'swap',
        attributes: {
          rel: "stylesheet preload prefetch",
        },
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    }, {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/locales`,
        name: `locale`
      }
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `Web development | Bart Fluitsma`,
        short_name: `Bart develops`,
        start_url: `/`,
        background_color: `#663399`,
        // This will impact how browsers show your PWA/website
        // https://css-tricks.com/meta-theme-color-and-trickery/
        // theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/logo-bart-fluitsma-web-design.svg`, // This path is relative to the root of the site.
      },
    },
    {
      resolve: `gatsby-plugin-react-i18next`,
      options: {
        localeJsonSourceName: `locale`, // name given to `gatsby-source-filesystem` plugin.
        languages: [`en`, `nl`],
        defaultLanguage: `en`,
        // if you are using Helmet, you must include siteUrl, and make sure you add http:https
        siteUrl: `https://bartfluitsma.com`,
        // you can pass any i18next options
        i18nextOptions: {
          interpolation: {
            escapeValue: false // not needed for react as it escapes by default
          },
          keySeparator: false,
          nsSeparator: false
        },
        pages: [
          {
            matchPath: '/:lang?/blog/:uid',
            getLanguageFromPath: true
          },
        ]
      }
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.dev/offline
    // `gatsby-plugin-offline`,
    {
      resolve: 'gatsby-plugin-sitemap',
      options: {
        excludes: ['/**/404', '/**/404.html'],
        query: `
            {
              site {
                siteMetadata {
                  siteUrl
                }
              }
              allSitePage(filter: {context: {i18n: {routed: {eq: false}}}}) {
                edges {
                  node {
                    context {
                      i18n {
                        defaultLanguage
                        languages
                        originalPath
                      }
                    }
                    path
                  }
                }
              }
            }
          `,
        serialize: ({ site, allSitePage }) => {
          return allSitePage.edges.map((edge) => {
            const { languages, originalPath, defaultLanguage } = edge.node.context.i18n;
            const { siteUrl } = site.siteMetadata;
            const url = siteUrl + originalPath;
            const links = [
              { lang: defaultLanguage, url },
              { lang: 'x-default', url }
            ];
            languages.forEach((lang) => {
              if (lang === defaultLanguage) return;
              links.push({ lang, url: `${siteUrl}/${lang}${originalPath}` });
            });
            return {
              url,
              changefreq: 'daily',
              priority: originalPath === '/' ? 1.0 : 0.7,
              links
            };
          });
        }
      }
    },
  ],
}

Upvotes: 5

Views: 1943

Answers (4)

glassonion1
glassonion1

Reputation: 29

I'm customizing the code for generating a multilingual sitemap as follows. In the actual production code, each language in the 'languages' variable is retrieved from the i18n configuration settings.

import type { GatsbyConfig } from 'gatsby'

const languages = ['en', 'fr', 'ja']

const config: GatsbyConfig = {
  plugins: [
    'gatsby-plugin-pnpm',
    'gatsby-plugin-postcss',
    {
      resolve: `gatsby-plugin-sitemap`,
      options: {
        excludes: ['/**/404', '/**/404.html'],
        serialize: ({ path }) => {
          // Get the page path without the language code.
          const pagepath = path.replace(/\/.*?\//, '/')
          // Generate XML links for each language.
          const xmlLinks = languages.map((lang: string) => ({
            rel: 'alternate',
            hreflang: lang,
            url: `/${lang}${pagepath}`
          }))
          xmlLinks.push({
            rel: 'alternate',
            hreflang: 'x-default',
            url: `${pagepath}`
          })

          let entry = {
            url: path,
            changefreq: 'daily',
            links: xmlLinks
          }

          return entry
        }
      }
    }
  ]
}

The generated XML looks as below.

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
  <url>
    <loc>https://sample.com/</loc>
    <changefreq>daily</changefreq>
    <xhtml:link rel="alternate" hreflang="en" href="https://sample.com/en/" />
    <xhtml:link rel="alternate" hreflang="ja" href="https://sample.com/ja/" />
    <xhtml:link rel="alternate" hreflang="x-default" href="https://sample.com/" />
  </url>
  <url>
    <loc>https://sample.com/en/</loc>
    <changefreq>daily</changefreq>
    <xhtml:link rel="alternate" hreflang="en" href="https://sample.com/en/" />
    <xhtml:link rel="alternate" hreflang="ja" href="https://sample.com/ja/" />
    <xhtml:link rel="alternate" hreflang="x-default" href="https://sample.com/" />
  </url>
  <url>
    <loc>https://sample.com/ja/</loc>
    <changefreq>daily</changefreq>
    <xhtml:link rel="alternate" hreflang="en" href="https://sample.com/en/" />
    <xhtml:link rel="alternate" hreflang="ja" href="https://sample.com/ja/" />
    <xhtml:link rel="alternate" hreflang="x-default" href="https://sample.com/" />
  </url>
</urlset>

Versions: Node: v19.4.0

"gatsby": "v19.4.0",
"gatsby-plugin-sitemap": "6.11.0"

Upvotes: 0

Asem Kakhi
Asem Kakhi

Reputation: 81

This worked for me without the need to create new types:

{
  resolve: "gatsby-plugin-sitemap",
  options: {
    excludes: ["/**/404", "/**/404.html"],
    resolveSiteUrl: () => siteUrl,
    query: `
      {
        allSitePage {
          edges {
            node {
              pageContext
            }
          }
        }
      }
    `,
    resolvePages: ({ allSitePage: { edges } }) => {
      return edges
        .filter(
          ({ node }) => !["/404/", "/404.html"].includes(node.pageContext.i18n.originalPath)
        )
        .map(({ node: { pageContext } }) => {
          const { languages, originalPath, path, defaultLanguage } = pageContext.i18n;
          const baseUrl = siteUrl + originalPath;
          const links = [{ lang: "x-default", url: baseUrl }];

          languages.forEach((lang) => {
            const isDefaultLang = lang === defaultLanguage;
            const isDefaultPath = path === originalPath;
            const isLangSubDir = path.includes(`${lang}/`);

            if (isDefaultLang && isDefaultPath) return;
            if (!isDefaultLang && isLangSubDir) return;

            links.push({
              lang,
              url: isDefaultLang ? baseUrl : `${siteUrl}/${lang}${originalPath}`,
            });
          });

          return {
            path,
            url: path === "/" ? siteUrl : `${siteUrl}/${path}`,
            changefreq: "daily",
            priority: originalPath === "/" ? 1.0 : 0.7,
            links,
          };
        });
    },
    serialize: (page) => page,
  },
}

Versions: Node: 18.13.0

"gatsby": "^5.3.3",
"gatsby-plugin-react-i18next": "^3.0.1",
"gatsby-plugin-sitemap": "^6.5.0",

Upvotes: 2

bebbi
bebbi

Reputation: 2539

The answer by Ferran Buireu ultimately was not the solution for OP. I had experienced the same issue and this solution would have solved his issue hadn't he abandoned it. Check this GitHub thread.

Your siteUrl issue just masks that the query was invalid, as the context is not available in gatsby >= 4 anymore, as you found out after fixing the siteUrl.

You may have used this query from the gatsby-plugin-react-i18next docs to support a sitemap.

In order to make it work, I found you have to 1. create the context yourself, and 2. adjust the queries

  1. Create your context through gatsby-node.js (credit wilsonvolker)
/**
 * Workaround for missing sitePage.context:
 * Used for generating sitemap with `gatsby-plugin-react-i18next` and `gatsby-plugin-sitemap` plugins
 * https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v3-to-v4/#field-sitepagecontext-is-no-longer-available-in-graphql-queries
 */
exports.createSchemaCustomization = ({ actions }) => {
    const { createTypes } = actions
    createTypes(`
    type SitePage implements Node {
      context: SitePageContext
    }
    type SitePageContext {
      i18n: i18nContext
    }
    type i18nContext {
        language: String,
        languages: [String],
        defaultLanguage: String,
        originalPath: String
        routed: Boolean
    }
  `)
}
  1. It looks like the serialize function proposed in the i18next doesn't work as-is anymore since it apparently receives a single node, not the full graphql response. So, a few changes in gatsby-config.js to make it work again (this assumes you have a global siteUrl variable available):
query: `
  {
    site {
      siteMetadata {
        siteUrl
      }
    }
    allSitePage(filter: {context: {i18n: {routed: {eq: false}}}}) {
      nodes {
        context {
          i18n {
            defaultLanguage
            languages
            originalPath
          }
        }
        path
      }
    }
  }
`,
serialize: (node) => {
  const { languages, originalPath, defaultLanguage } = node.context.i18n
  const url = siteUrl + originalPath
  const links = [
    { lang: defaultLanguage, url },
    { lang: 'x-default', url },
  ]
  languages.forEach((lang) => {
    if (lang === defaultLanguage) return
    links.push({ lang, url: `${siteUrl}/${lang}${originalPath}` })
  })
  return {
    url,
    changefreq: 'daily',
    priority: originalPath === '/' ? 1.0 : 0.7,
    links,
  }
},

Upvotes: 3

Ferran Buireu
Ferran Buireu

Reputation: 29315

The error is thrown by gatsby-plugin-sitemap. Try adding the resolveSiteUrl method in your configuration like:

const siteUrl = process.env.URL || `https://fallback.net`

resolveSiteUrl: () => siteUrl,

Applied to your code:

const siteUrl = process.env.URL || `https://fallback.net`

module.exports = {
  plugins: [
    {
      resolve: "gatsby-plugin-sitemap",
      options: {
        excludes: ["/**/404", "/**/404.html"],
        query: `
            {
              site {
                siteMetadata {
                  siteUrl
                }
              }
              allSitePage(filter: {context: {i18n: {routed: {eq: false}}}}) {
                edges {
                  node {
                    context {
                      i18n {
                        defaultLanguage
                        languages
                        originalPath
                      }
                    }
                    path
                  }
                }
              }
            }
          `,
        resolveSiteUrl: () => siteUrl,
        serialize: ({ site, allSitePage }) => {
          return allSitePage.edges.map((edge) => {
            const { languages, originalPath, defaultLanguage } =
              edge.node.context.i18n;
            const { siteUrl } = site.siteMetadata;
            const url = siteUrl + originalPath;
            const links = [
              { lang: defaultLanguage, url },
              { lang: "x-default", url },
            ];
            languages.forEach((lang) => {
              if (lang === defaultLanguage) return;
              links.push({ lang, url: `${siteUrl}/${lang}${originalPath}` });
            });
            return {
              url,
              changefreq: "daily",
              priority: originalPath === "/" ? 1.0 : 0.7,
              links,
            };
          });
        },
      },
    },
  ],
};

Change the siteUrl and the fallback URL accordingly if you are not setting it in your environment file

Upvotes: 3

Related Questions