2023-7-2

NextJS App RouterでSitemap, Robot.txt, RSSを配置する

フロントエンド

このブログにsitemap, robots.txt, RSSなどを配置したいが、旧来のPagesRouterとAppRouterのドキュメントやブログが混ざっていてなかなかまとまった情報が無い。 もろもろ調べたところ以下の通りに対応するのが一番簡単そうだった。

  • sitemap: NextJS組み込みのsitemap関数を利用
  • robots.txt: NextJS組み込みのrobot関数を利用
  • RSS: route.tsを使って自前でレンダリング

SitemapとRobot.txt

この2つに関してはNextJSに予め組み込まれた関数が存在しているので、それを使うのが良さそう。

サイトマップの例

このサイトはnext exportして静的ファイルとしてホスティングしているのでトップページのlastModifiedはビルドした時刻になるはず。 普通にホスティングする際にはデータ取得部分がStaticかDynamicかによって挙動が変わってしまうので少し注意が必要になるかもしれない。(未検証)

typescript
Copied!
import { MetadataRoute } from "next";
import { getSortedBlogList } from "../lib/blogs";
import * as cst from "../lib/const";
import urlJoin from "url-join";

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const records: MetadataRoute.Sitemap = [];
  const blogs = await getSortedBlogList();
  records.push({
    url: cst.SITE_URL,
    lastModified: new Date(),
  });
  blogs.map((blog) => {
    records.push({
      url: urlJoin(cst.SITE_URL, "blog", blog.slug),
      lastModified: blog.date,
    });
  });
  return records;
}

robots.txtの例

typescript
Copied!
import { MetadataRoute } from "next";
import * as cst from "../lib/const";
import urlJoin from "url-join";

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: "*",
      allow: "/",
    },
    sitemap: urlJoin(cst.SITE_URL, "sitemap.xml"),
  };
}

参考

RSS

RSSの生成機能はNextJSの組み込みでは存在しないので自前で実装する必要がある。 XMLを生成する必要があるため通常のpage.tsxでレンダリングするのではなく、 route.tsを利用してカスタムリクエストハンドラを作成する。 このサイトを参考にして、 rss.xml/route.tsというファイルを作成し以下のようなコードを記載する。

typescript
Copied!
import Rss from "rss";
import { getSortedBlogList } from "@/lib/blogs";
import * as cst from "../../lib/const";
import urlJoin from "url-join";

export async function GET() {
  const feed = new Rss({
    title: cst.TITLE,
    description: cst.DESCRIPTION,
    feed_url: urlJoin(cst.SITE_URL, "rss.xml"),
    site_url: cst.SITE_URL,
    language: "ja",
  });

  const blogList = await getSortedBlogList();
  blogList.forEach((blog) => {
    feed.item({
      title: blog.title,
      description: blog.summary,
      url: urlJoin(cst.SITE_URL, "blog", blog.slug),
      date: blog.date,
    });
  });

  return new Response(feed.xml(), {
    headers: {
      "Content-Type": "application/xml",
    },
  });
}