QLITRE DIALY

HonoXとmicroCMSのブログでsitemapをサクッと動的に生成

2025年03月04日

タイトルの通り。

この日記サイトはmicroCMSで作成した記事をHonoXでAPIで取得、そしてCloudflareにデプロイ、という構成で動いている。

sitemapの配信について、これまではローカル側でPythonで書いたスクリプトを動かして静的なsitemap.xmlを生成していた。

静的ファイルの配信なので表示は早いのだけど、記事を更新するたびにローカル側からデプロイするのがちょっと面倒だな、ということを感じていた。

ネットで調べてみるとZennに参考になる記事があった。

HonoXで作ったサイト(SSR)に雑にsitemap.xmlを追加する

route/sitemap.xml.tsを作成するだけで、エンドポイントを生やせるらしい。この発想はなかった。

で、honoではデータを返すときにc.textで{"Content-Type": "application/xml"}というように指定ができるので、ということはsitemapの文字情報を適当に作って、返してやればいい。

ここまでくるとやることは非常にシンプルで、microCMSの記事情報を全件取得するような形でAPIを投げて、文字を作ってしまえばいい。

以下のようなコードになった。

import { createRoute } from 'honox/factory'
import { config } from '../settings/siteSettings'
import type { MicroCMSListResponse } from 'microcms-js-sdk'
import type { Post } from '../types/blog'
import { MicroCMSClient } from '../libs/microcmsClient'


export default createRoute(async (c) => {
  const client = new MicroCMSClient(c.env.SERVICE_DOMAIN, c.env.API_KEY)
  const r = await client.getListResponse<MicroCMSListResponse<Post>>('post', { limit: 0 })
  const limit = 50
  const tot = r.totalCount
  const cnt = Math.ceil(tot / limit)
  const urls: string[] = []
  const baseUrl = config.siteURL
  for (let i = 0; i < cnt; i++) {
    const offset = i * limit
    const postList = await client.getListResponse<MicroCMSListResponse<Post>>('post', { limit: limit, offset: offset, fields: 'id,updatedAt' })
    for (const post of postList.contents) {
      urls.push(`
      <url>
        <loc>${baseUrl}/post/${post.id}</loc>
        <lastmod>${post.updatedAt.split("T")[0]}</lastmod>
      </url>`)
    }
  }

  const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
      <loc>${baseUrl}/</loc>
    </url>
    ${urls.join("")}
  </urlset>`;
  return c.text(sitemap, 200, { "Content-Type": "application/xml" });
})

microCMSのリクエストリミットについて、以前は上限値がなかったと記憶しているが、仕様変更が入りlimitの上限が100となった。

【要対応】APIのリクエスト制限強化等の仕様変更のお知らせ

そのため、こういう風に全件を取得したい場合は、まずはlimit=0でリクエストを投げて件数を取得して、回数を事前に計算する。

自分は一回のリクエストを50にすると設定した。なので、あとはforループで50件ずつoffset値をずらしながら取得して、文字列を配列に格納していく…という方法で実装した。

最初のリクエストをなくして一発目から記事idを取得していくこともできるが、こっちの方が自分にとっては処理が分かりやすい。

しかし、とてもシンプルに実装ができて驚いた。

以前に使っていたフレームワークNuxt.jsだと、sitemapの生成は確か外部モジュールを使うような感じだったと思う。

外部モジュールを使わなければいけないと、じゃあ何を入れればいいのか、とか、それぞれの使い方はどうなんだ、など調査するべきことが意外と多い。調査して苦労の末に実装をして動いている間はいいが、バージョンアップしたらなぜか動かなくなったみたいなこともありがちである。そして、sitemapの生成方法を検証しているだけで、気づいたら一日が経っていた、そうして疲れた末に、俺のサイトにsitemapの配信なんか必要はなかったんだ、大事なのは記事の内容だから、そんな風に開き直ることでやり過ごそうとする。そうしてsitemapの生成をしないままにしてしまう。実際に自分も以上のようなプロセスで挫折した経験がある。

Honoの場合だと上に書いたようにフレームワークの標準機能で簡単に実装ができる。textを作って返すだけ。

標準機能でやりたいことがシンプルにできた、たぶん他にも色々できるんだろう、そうすると色んな調査をしたり互換性を気にしたり、バージョンアップに合わせて実装を変えたりする必要がない。よく言われているDXが良いというのをここに感じる。

配信しているサイトマップは以下のリンクより。

https://qlitre-dialy.ink/sitemap.xml

ではでは。