How to Generate Sitemap Dynamically in NodeJS

2 min read
How to Generate Sitemap Dynamically in NodeJS

Node js is an open-source, server-side runtime environment that is JavaScript based and used to run JavaScript-based applications. It can execute JavaScript code on a server hence useful for creating server-based web applications.

Nodejs is very efficient, fast and cross-platform and can be used as a backend for many frontend frameworks like NextJS, react and many more.

A sitemap is an XML file that contains information about pages, videos, images and other important links on your website which you want to be indexed. A sitemap can contain references to other sitemaps also. You can have multiple sitemaps also but then you need to mention them in the robots.txt file.

Sitemap has always been an important aspect of SEO, while it’s not mandatory to have but it helps search engines crawl and index your site faster and more efficiently.

Generating the sitemap

To generate a sitemap in nodejs, we need to create a script that when run will create the sitemap containing all the links. So we can create a folder called scripts in our application’s root directory and place the script in it.

The next thing we need is an XML writer since the sitemap is an XML. For this, we will use a library called xmlbuilder which is very fast, lightweight and efficient.

While it’s easy to add static pages to the sitemap, it’s a bit tricky to add dynamic pages to the sitemap. Let’s first see how to add static pages to the sitemap using xmlbuilder.

Adding static pages to the sitemap

A typical sitemap has the following XML tags and structure.

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>www.example.com/some-url</loc>
    <lastmod>2023-01-29</lastmod>
    <priority>0.80</priority>
  </url>
  <url>
    <loc>www.example.com/another-url</loc>
    <lastmod>2023-01-29</lastmod>
    <priority>0.80</priority>
  </url>
</urlset>

Suppose we have three static pages called contactabout and privacy in our application. Let’s write the code for it in our script called static.js

const builder = require('xmlbuilder');

const HOST = 'www.example.com'
const pages = ['about', 'contact', 'privacy']

const urlset = builder.create('urlset')
    .dec('1.0', 'UTF-8')
    .att('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9')

for (const page of pages) {
    const url = urlset.ele('url');
    url.ele('loc', '', HOST + page)
    url.ele('lastmod', '', '2022-12-31')
    url.ele('priority', '', '0.80')
}

const xml = urlset.end({pretty: true});
console.log(xml)

Run the script manually with the following command from the project root folder.

node scripts\static.js

and here is the output

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>www.example.com/about</loc>
    <lastmod>2022-12-31</lastmod>
    <priority>0.80</priority>
  </url>
  <url>
    <loc>www.example.com/contact</loc>
    <lastmod>2022-12-31</lastmod>
    <priority>0.80</priority>
  </url>
  <url>
    <loc>www.example.com/privacy</loc>
    <lastmod>2022-12-31</lastmod>
    <priority>0.80</priority>
  </url>
</urlset>

Adding dynamic pages to the sitemap

Adding dynamic pages is almost the same as adding static pages except for the point that in dynamic pages, you first need to fetch all the pages from the REST APIs or database. If you have rest APIs already built then you can call them via axios or node’s http module to call rest APIs.

For the scope of this article, let’s assume that you have made the API call and have all the posts in hand.

Now traverse through all the posts and add each link to the sitemap, see the code below.

const builder = require('xmlbuilder');

const HOST = 'www.example.com'
const posts = [
    {slug: 'blog-post-1', date: new Date().toDateString()},
    {slug: 'blog-post-2', date: new Date().toDateString()},
    {slug: 'blog-post-3', date: new Date().toDateString()}
] //fetched from REST API

const urlset = builder.create('urlset')
    .dec('1.0', 'UTF-8')
    .att('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9')

for (const post of posts) {
    const url = urlset.ele('url');
    url.ele('loc', '', HOST + '/' + post.slug)
    url.ele('lastmod', '', formatDate(post.date))
    url.ele('priority', '', '0.80')
}

const xml = urlset.end({pretty: true});
console.log(xml)


function formatDate(date) {
    const date0 = new Date(date)
    let dd = date0.getDate()
    if (dd < 10) {
        dd = '0' + dd
    }
    let mm = date0.getMonth() + 1
    if (mm < 10) {
        mm = '0' + mm
    }
    return date0.getFullYear() + '-' + mm + '-' + dd
}

The function formatDate is used to format the date to the format that is acceptable in sitemap xml. Sitemap accepts the date format as ‘YYYY-MM-DD’.

If you run the script, you will see the following output. You can run the script via the same node command.

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>www.example.com/blog-post-1</loc>
    <lastmod>2023-01-29</lastmod>
    <priority>0.80</priority>
  </url>
  <url>
    <loc>www.example.com/blog-post-2</loc>
    <lastmod>2023-01-29</lastmod>
    <priority>0.80</priority>
  </url>
  <url>
    <loc>www.example.com/blog-post-3</loc>
    <lastmod>2023-01-29</lastmod>
    <priority>0.80</priority>
  </url>
</urlset>

Complete code with file writing

So far we have seen how to add static and dynamic pages to the sitemap. The next step is to write the sitemap to the actual XML file. For this purpose, we will make use of the node’s fs and path module.

The path module is used to navigate through the file system and returns a location.

The fs module contains functions for reading/writing to the file system and much more.

Here is the complete code for adding all the pages to the sitemap and writing this to the sitemap.xml file in the public folder in your project. The name of the script is complete.js.

const builder = require('xmlbuilder');
const fs = require('fs');
const path = require('path');

const HOST = 'www.example.com'
const pages = ['about', 'contact', 'privacy']
const posts = [
    {slug: 'blog-post-1', date: new Date().toDateString()},
    {slug: 'blog-post-2', date: new Date().toDateString()},
    {slug: 'blog-post-3', date: new Date().toDateString()}
]

const urlset = builder.create('urlset')
    .dec('1.0', 'UTF-8')
    .att('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9')

for (const page of pages) {
    const url = urlset.ele('url');
    url.ele('loc', '', HOST + '/' + page)
    url.ele('lastmod', '', '2022-12-31')
    url.ele('priority', '', '0.80')
}
for (const post of posts) {
    const url = urlset.ele('url');
    url.ele('loc', '', HOST + '/' + post.slug)
    url.ele('lastmod', '', formatDate(post.date))
    url.ele('priority', '', '0.80')
}
const xml = urlset.end({pretty: true});
fs.writeFileSync(path.join(process.cwd(), 'public', 'sitemap.xml'), xml);


function formatDate(date) {
    const date0 = new Date(date)
    let dd = date0.getDate()
    if (dd < 10) {
        dd = '0' + dd
    }
    let mm = date0.getMonth() + 1
    if (mm < 10) {
        mm = '0' + mm
    }
    return date0.getFullYear() + '-' + mm + '-' + dd
}

Executing the script post build

Although you can execute the sitemap script manually anytime you want, it’s preferable to execute it after the build has been completed.

  • Running the script manually

To run it manually, run the following command from the project root folder.

node scripts\complete.js
  • Automatically running the script post build

To run it post build, you need to add a custom command called postbuild in your package.json file and mention the script to be executed. This will ensure that this script gets executed every time a post command is executed. See the command below.

"postbuild": "node scripts/complete.js"

and that’s it. Now everytime you build your applicaiton, your sitemap will be written inside public folder with latest posts.