Next.jsで動的にsitemap.xmlを生成する
SSGのサービスの場合はsitemap.xmlを生成するスクリプトをビルド時に実行することで、sitemap.xmlを最新に保つことが出来るが、Next.js9.3から利用可能になったIncremental Static Regenerationを用いたサービスの場合は、ビルド後にコンテンツが動的に増減するためsitemap.xmlの生成に一工夫が必要になる。
このsitemap.xmlはSSRで生成するしかないので[[pages/sitemap.xml.ts]]を作成し、getServerSideProps内でxmlを生成する。
ブログ記事(posts)を例とする。
sitemap生成時は全記事のURLが必要になるので、記事一覧取得APIを繰り返し呼び出して全記事を取得するか、sitemap専用のAPIを作る必要がある。
今回はsitemap専用のAPIを作ることにする。
↓のようにgetServerSideProps内でsitemap.xmlを組み立てて、res.writeでレスポンスを返すようにしている。
PageComponentは使っていないが、何かしらのJSXを返す必要があるので空のComponentを返すようにした。
これで記事の増減があった場合にも動的にsitemap.xmlが生成される。
// pages/sitemap.xml.ts
import { GetServerSidePropsContext } from 'next'
import React from 'react'
import { someApiClient } from '@/lib/someApiClient'
import { GetSitemapResponse } from '@/lib/GetSitemapResponse'
const createSitemap = (response: GetSitemapResponse) => {
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0"
xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
${response.posts
.map(({ slug, publishedAt }) => {
return `
<url>
<loc>${`${siteUrl}/posts/${slug}`}</loc>
<lastmod>${publishedAt}</lastmod>
</url>
`
})
.join('')}
</urlset>
`
}
export const getServerSideProps = async ({ res }: GetServerSidePropsContext) => {
const response = await someApiClient('/api/sitemap').catch((e) => {
console.error(e.message)
return {
posts: [],
}
})
res.setHeader('Content-Type', 'text/xml')
res.write(createSitemap(response))
res.end()
return { props: {} }
}
const Sitemap = () => {
return <></>
}
export default Sitemap
同じようにしてgetServerSideProps内でRSSを生成すればRSS配信もできる。