Skip to content

Latest commit

 

History

History
199 lines (163 loc) · 5.81 KB

CustomSitemap.adoc

File metadata and controls

199 lines (163 loc) · 5.81 KB

Custom Sitemap

In this chapter we will describe how to implement a Rest interface so that you can define your own sitemap.

Please note the general preface on how to define CustomRoutes. Here we build directly on it.

As a starting point you can use the file customSitemap.ts with the content:

import express, { Request, Response } from 'express'
// eslint-disable-next-line import/named
import { FSXAMiddlewareContext } from 'fsxa-nuxt-module'

export default {
  async handler(
    context: FSXAMiddlewareContext,
    request: Request,
    response: Response
  ) {
    const app = express()

    return app(request, response)
  },
  route: '/sitemap'
}

First of all, you want to display all the data from the navigation service in your sitemap. To do this, after initializing the app, you have to create a new GET endpoint and make it asynchronous so you can use await and not have to work with promise.

app.get('/', async (req, res) => {
  const host = [req.protocol, '://', req.headers.host].join('')
  const navigation = await context.fsxaAPI.fetchNavigation(null, 'de_DE')
  const locations = Object.keys(navigation?.seoRouteMap || [])

  res.set('Content-Type', 'text/xml')
  res.send(`<?xml version="1.0" encoding="UTF-8"?>
      <urlset>
      ${locations
        .map(
          (location) => `
            <url><loc>${host}${location}</loc></url>
            `
        )
        .join('\n')}
            </urlset>`)
})

Here you first compose the host by getting the protocol e.g. http and the host from the headers e.g. localhost:3000. Next get the navigation with the FSXA-API. The FSXA API is available to us through the context. There you don’t have an initalPath, so enter null as first parameter and as locale enter 'de_DE' to get all german routes back.

From the data of the navigation service you need only the information from the seoRouteMap and from it only the keys, because these are the URLs.

In the response, you need to set the Content-Type to text/xml because the sitemap is rendered in XML.

Then you compose the sitemap in the response. Define the version and the encoding and as a frame a <urlset>. You need to map the individual entries that are in the constant locations by enclosing each entry in <url><loc>. As content use the host, which has been assembled before and the entry itself.

Send this string as a response.

In this example, in addition to the content that comes from the navigation service, we show how to add the routes that you can define under ~/pages.

When the Nuxt application starts, a JSON file is generated under ~/dist/routes.json in which all routes are listed. Use this file so that you can integrate the routes into our sitemap.

In the endpoint, you want to read the contents of this file and filter out the fsxa-page entry.

Therefore import the following packages:

import fs from 'fs'
import path from 'path'

However, these do not need to be installed as they are part of Node’s default API.

const routesFile = fs.readFileSync(
  path.resolve(process.cwd(), './dist/routes.json'),
  {
    encoding: 'UTF-8'
  }
)
let routes: any[] = JSON.parse(routesFile)
routes = routes.filter((item) => item.name !== 'fsxa-page')

So that you can replace the any[], create an interface and write it directly after the imports into the customSitemap.ts file.

interface RouteItem {
  name: string
  path: string
}

Change the type:

let routes: RouteItem[] = JSON.parse(routesFile)

Add these routes to the locations.

const locations = [...Object.keys(navigation?.seoRouteMap || []), ...routes]

If you try this now and create a few pages that have the same name as the pages you get back from the navigation service, you find that they now appear twice in the sitemap.

You need to put the routes in a consistent format. To do this, make all the letters lowercase and add a / to the local routes.

To avoid having duplicates, create a Set.

const locations = [
  ...new Set([
    ...Object.keys(navigation?.seoRouteMap || []).map((route) =>
      route.toLowerCase()
    ),
    ...routes.map((route) => route.path.toLowerCase() + '/')
  ])
]

The final version of the customSitemap.ts file looks like this:

import express, { Request, Response } from 'express'
import fs from 'fs'
import path from 'path'
// eslint-disable-next-line import/named
import { FSXAMiddlewareContext } from 'fsxa-nuxt-module'

interface RouteItem {
  name: string
  path: string
}

export default {
  async handler(context: FSXAMiddlewareContext, req: Request, res: Response) {
    const app = express()
    app.get('/', async (request, response) => {
      const host = [req.protocol, '://', req.headers.host].join('')
      const routesFile = fs.readFileSync(
        path.resolve(process.cwd(), './dist/routes.json'),
        {
          encoding: 'UTF-8'
        }
      )
      let routes: RouteItem[] = JSON.parse(routesFile)
      routes = routes.filter((item) => item.name !== 'fsxa-page')
      const navigation = await context.fsxaAPI.fetchNavigation(null, 'de_DE')
      const locations = [
        ...new Set([
          ...Object.keys(navigation?.seoRouteMap || []).map((route) =>
            route.toLowerCase()
          ),
          ...routes.map((route) => route.path.toLowerCase() + '/')
        ])
      ]

      res.set('Content-Type', 'text/xml')
      res.send(`<?xml version="1.0" encoding="UTF-8"?>
      <urlset>
      ${locations
        .map(
          (location) => `
            <url><loc>${host}${location}</loc></url>
            `
        )
        .join('\n')}
            </urlset>`)
    })
    return app(req, res)
  },
  route: '/sitemap'
}