Nuxt3のSSRでmicroCMSのsitemap.xmlを出力する
Nuxt3のsitemap.xmlを出力する場合は事前生成することもできますが、複雑な場合はSSRで作成したほうが融通が効きます。
You can fetch data from your database or another server, create APIs, or even generate static server-side content like a sitemap or a RSS feed - all from a single codebase.
serverディレクトリの説明にはsitemapのオンデマンド生成も利用目的としてあげられていますのでserverディレクトリを利用するのがよいでしょう。
microCMSのAPIでsitemap.xmlを出力する
今回はmicroCMSを利用している場合にsitemap.xmlを出力する方法を解説していきます。
microCMSのブログテンプレートからAPIを作成した場合、ブログ(blogs)とカテゴリ(categories)というコンテンツが作成されるのでそのページ一覧が表示されるイメージで作成していきます。
sitemapを分割して生成
microCMSの場合は1度のリクエストで取得できるコンテンツの数は100が上限です(一部例外があり)。
そのため記事数が1500件だと15回のリクエストが必要です。
再帰的に取りに行っても良いのですがリクエストが集中するとレスポンスが遅くなるので、ページを分割することで1ページ1リクエストのシンプルな構成が可能にしたほうが良いと思います。
生成するsitemapは以下のように分割しました。
- /sitemap.xml
- /sitemap/blogs.xml
- /sitemap/blogs-1.xml
- /sitemap/blogs-2.xml
- /sitemap/categories.xml
- /sitemap/categories-1.xml
- /sitemap/categories-2.xml
- /sitemap/blogs.xml
/sitemap.xml
/sitemap.xml
は以下のように静的に各サイトマップインデックスのURLが記載されるようにします。
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://example.com/sitemap/blogs.xml</loc>
</sitemap>
<sitemap>
<loc>https://example.com/sitemap/categories.xml</loc>
</sitemap>
</sitemapindex>
Nuxt.js側では静的URLなのでpublic/sitemap.xml
に用意しても良いですが、環境変数等が利用したい場合などは server/routes/sitemap.xml.ts
に以下のような記述を行います。
export default defineEventHandler(() => {
const sitemapXml = `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>${process.env.BASE_URL}/sitemap/blogs.xml</loc>
</sitemap>
<sitemap>
<loc>${process.env.BASE_URL}/sitemap/categories.xml</loc>
</sitemap>
</sitemapindex>
`
return new Response(sitemapXml, {
headers: {
'Content-Type': 'text/xml'
},
});
})
環境変数などは.envで定義しておいてください。
BASE_URL = https://example.com
/sitemap/blogs.xml と /sitemap/categories.xml
/sitemap/blogs.xml
と /sitemap/categories.xml
は各サイトマップへのサイトマップインデックスが記載されます。
コンテンツ量が100件未満ならサイトマップインデックスではなくて直接サイトマップを記載してもよいですが、今後増加することを考えるとサイトマップインデックスを置くと良いでしょう。
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://example.com/sitemap/blogs-1.xml</loc>
</sitemap>
<sitemap>
<loc>https://example.com/sitemap/blogs-2.xml</loc>
</sitemap>
<sitemap>
<loc>https://example.com/sitemap/blogs-3.xml</loc>
</sitemap>
</sitemapindex>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://example.com/sitemap/categories-1.xml</loc>
</sitemap>
<sitemap>
<loc>https://example.com/sitemap/categories-2.xml</loc>
</sitemap>
<sitemap>
<loc>https://example.com/sitemap/categories-3.xml</loc>
</sitemap>
</sitemapindex>
Nuxt.js側では以下のような動的ルーティングで生成を行います。
URLをもとにblogs.xml
とcategories.xml
の場合のみサイトマップインデックスを生成しています。
APIを一度叩けばコンテンツの総数がわかるのでここでは最小数の取得でよいでしょう。
export default defineEventHandler(async (event) => {
const file = getRouterParam(event, 'file') || ''
let sitemapXml: string | undefined
if (file === 'blogs.xml') {
sitemapXml = await generateSitemapIndex(file.replace('.xml', ''))
} else if (file === 'categories.xml') {
sitemapXml = await generateSitemapIndex(file.replace('.xml', ''))
}
if (sitemapXml) {
return new Response(sitemapXml, {
headers: {
'Content-Type': 'text/xml'
},
});
}
})
/**
* マイクロCMSのデータを取得
*/
export const getMicroCmsData = async ({ target, limit = 100, offset = 0, fields = 'id' }: { target: string; limit?: number, offset?: number, fields?: string }) => {
const response = await fetch(`${process.env.MICROCMS_API_URL}/${target}?limit=${limit}&offset=${offset}&fields=${fields}`, {
headers: { 'X-MICROCMS-API-KEY': process.env.MICROCMS_API_KEY || '' },
})
return await response.json();
}
/**
* インデックスサイトマップ作成
*/
const generateSitemapIndex = async (target: string) => {
// 総数の取得
const { totalCount } = await getMicroCmsData({ target, limit: 1 })
const maxPage = Math.ceil(totalCount / 100)
const siteMapXMLs = []
for (let page = 1; page <= maxPage; page++) {
siteMapXMLs.push(`
<sitemap>
<loc>${process.env.BASE_URL}/sitemap/${target}-${page}.xml</loc>
</sitemap>
`)
}
return `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${siteMapXMLs.join('')}
</sitemapindex>
`
}
/sitemap/blogs-{page}.xml と /sitemap/categories-{page}.xml
詳細のサイトマップを作成していきましょう。詳細のサイトマップでは各ページのURLが最大100件記載されます。
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/blogs/s73yzb0-y/</loc>
</url>
<url>
<loc>https://example.com/blogs/asdfae-y/</loc>
</url>
<url>
<loc>https://example.com/blogs/s73yzbadfy/</loc>
</url>
</urlset>
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/categories/s73yzb0-y/</loc>
</url>
<url>
<loc>https://example.com/categories/asdfae-y/</loc>
</url>
<url>
<loc>https://example.com/categories/s73yzbadfy/</loc>
</url>
</urlset>
先ほど作成したserver/routes/sitemap/[file].ts
に追記していきます。
defineEventHandlerにはURL判定を追加してgenerateSitemapPage
を実行しています。
generateSitemapPage
は新たにページサイトマップを作成する関数として作成しています。
export default defineEventHandler(async (event) => {
const file = getRouterParam(event, 'file') || ''
let sitemapXml: string | undefined
if (file === 'blogs.xml') {
sitemapXml = await generateSitemapIndex(file.replace('.xml', ''))
} else if (file === 'categories.xml') {
sitemapXml = await generateSitemapIndex(file.replace('.xml', ''))
+ } else if (/^(blogs|categories)-([0-9]+).xml$/.test(file)) {
+ const matches = file.match(/^(blogs|categories)-([0-9]+).xml$/);
+ sitemapXml = await generateSitemapPage(matches![1], Number(matches![2]))
}
if (sitemapXml) {
return new Response(sitemapXml, {
headers: {
'Content-Type': 'text/xml'
},
});
}
})
(中略、以下は追記)
/**
* ページサイトマップ作成
*/
const generateSitemapPage = async (target: string, page: number) => {
const { contents } = await getMicroCmsData({ target, offset: (page - 1) * 100, fields: "id" })
const siteMapXMLs = contents.map((page: { id: string }) => {
return `
<url>
<loc>${process.env.BASE_URL}/${target}/${page.id}</loc>
</url>
`
})
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${siteMapXMLs.join('')}
</urlset>
`
}
以上で完成です。
これでNuxt3のSSRでmicroCMSのsitemap.xmlを出力することができるようになります。
Discussion