sitemap と rss を作った

contentlayer の allPosts が便利


sitemap

sitemap とはウェブサイトの構造、配置を示すファイルやページのこと。 検索エンジンに登録し、このページを参照してページをインデックスしていくのであったほうが良い。

sitemap の実装

開発にあたり sitemap.xml の存在は知っていたものの自分で作るのは初めてだったので sitemap.org に目を通した。 また、利用しているアプリケーションフレームワークに沿った実装をすべきと、next.js のドキュメントを検索した。 すぐにMetadataRoute.Sitemapが見つかったので 提供されているAPIをそのまま使うことにした。

すべての記事をインデックスさせるためには記事のメタデータを取得する必要があるので contentlayerallPosts を使った。便利すぎる。

おおまかな処理シーケンスは以下の通り。

  1. すべての記事を取得する
  2. 1 を走査して sitemap.xml に必要な記事データを作る
  3. 静的な情報と 2 を結合する

以下が実際のコード。

import { MetadataRoute } from "next";
import { allPosts } from "contentlayer/generated";

export default function sitemap(): MetadataRoute.Sitemap {
  const blogPosts = allPosts.map((post) => ({
    url: `https://${SITE_DOMAIN}${post.slug}`,
    lastModified: post.modDate || post.date,
    changeFrequency: "daily" as const,
    priority: 0.7,
  }));

  return [
    {
      url: `https://${SITE_DOMAIN}`,
      lastModified: new Date(),
      changeFrequency: "yearly",
      priority: 1,
    },
    {
      url: `https://${SITE_DOMAIN}/about`,
      lastModified: new Date(),
      changeFrequency: "monthly",
      priority: 0.8,
    },
    {
      url: `https://${SITE_DOMAIN}`,
      lastModified: new Date(),
      changeFrequency: "weekly",
      priority: 0.5,
    },
    ...blogPosts,
  ];
}

rss フィードの実装

RSS の実装も sitemap と同じような考え方で作れば良い。 api のルーティング を参考にして app/feed.xml/route.ts に実装していく。 URLは nabetama.com/api/feed.xml になるのが気になったので、next.config.js に以下のように書きリライトしておいた。

async rewrites() {
  return [
    {
      source: '/feed.xml',
      destination: '/api/feed.xml',
    },
  ]
},

next.js 公式では RSS を作る API は提供されてないので自分で実装する。 RSS の仕様は RSS 2.0 仕様 が公式だが google がかいつまんで説明しているものがあるので、それに目を通した。

上記仕様に沿った XML を手で構築していくのは大変なので rss という npm package を使う。 RSS 2.0 の仕様に沿った XML を簡単に作ってくれる。

npm install rss

処理シーケンスはさきほどの sitemap と同じような形になる。 rssでは記事全文を配信するため generateFeed() という関数にてフィードの構築に必要なデータを作成し、 api の実装はそれを受け取り、適切なヘッダーを付与して返却すればよい。

import { generateFeed } from 'lib/feed'

export async function GET() {
  const resp = await generateFeed()

  return new Response(resp, {
    headers: {
      'Content-Type': 'application/xml; charset=utf-8',
    },
  })
}

さいごに

sitemap, RSS それぞれがどういうものか、漠然とした知識はあったが 開発するにあたって仕様を読んだのは初めてだったので学びがあった。 とにかく公式ドキュメントを読むクセをつけておくのは重要だと思っているので、 今後もなにかしらの仕様に沿ったものを作るときは時間をかけても公式ドキュメントに目を通してから作るように心がけたい。