Next.js Route Handlers ライブラリなしでRSSフィードを作る

Next.js Route Handlers ライブラリなしでRSSフィードを作る

2025/04/06 公開

はじめに

今回は、自分のブログにRSSを導入しようと思います。今はSNSがあるので、わざわざRSSリーダーを使う機会は減ったかもしれませんが、一部の人にとっては意味あるものだと思うので実装してみました。

RSSとは

まずRSS(Rich Site Summary、Really Simple Syndication)とは、Webサイトの新着記事や、更新情報の要約、見出しなどを配信する技術で、XML(eXtensible Markup Language)ベースの文書フォーマットの一種です。RSSリーダーと呼ばれるツールや、RSSに対応したブラウザを使用することで、Webサイトにアクセスしなくても、新着記事や更新情報を自動的に取得して、興味のある記事を簡単に閲覧できます。

実装

ネットやChatGPTに聞いてもfeednode-rssを使ってやりましょうという記事が出てきましたが、ライブラリは全くメンテナンスされていないですし、RSSの仕組み自体はそんなに難しいものだと思ったので、ライブラリは特に使わず実装しました。

RSS生成の手順

Next.jsのRoute Handlersを使って実装します。

  1. 記事データの取得: CMS(私のブログはNotionで管理しているためNotion API)から記事の一覧を取得。Notion APIの扱い方はこの記事では取り扱いません。
  2. RSSのXML生成: RSSの文字列を生成。
  3. API作成: app/rss.xml.route.ts でRSSフィードを作成。
  4. ヘッダーにリンクを追加: RSSのリンクを追加。

コード

app/rss.xml/route.ts

import { Client } from "@notionhq/client"; import { NextResponse } from "next/server"; const notion = new Client({ auth: process.env.NOTION_TOKEN, }); const getArticles = async () => { const filterObject = { property: "status", status: { equals: "published", }, }; const response = await notion.databases.query({ database_id: process.env.NOTION_DATABASE_ID as string, sorts: [ { // 公開日の降順に並び替える property: "published_at", direction: "descending", }, ], filter: filterObject, }); const posts = response.results; const postsProperties = posts.map((post: any) => { const title = post.properties.title.title[0]?.plain_text; const summary = post.properties.summary.rich_text[0]?.plain_text; const slug = post.properties.slug.rich_text[0]?.plain_text; const publishedAt = post.properties.published_at.date.start; return { title, slug, publishedAt, summary }; }); return postsProperties; }; export async function GET() {  // 1. Notion APIから記事一覧を取得 const articles = await getArticles(); // 2. RSSのXML生成 const rss = `<?xml version="1.0" encoding="UTF-8" ?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <channel> <title>br-to DevLog</title> <link>${process.env.NEXT_PUBLIC_BASE_URL}</link> <description>This blog is br-to's devlog.</description> <atom:link href="${process.env.NEXT_PUBLIC_BASE_URL}/rss.xml" rel="self" type="application/rss+xml" /> <language>ja</language> ${articles .map( ({ title, slug, publishedAt, summary }) => ` <item> <title>${title}</title> <link>${process.env.NEXT_PUBLIC_BASE_URL}/blog/${slug}</link> <guid isPermaLink="true">${process.env.NEXT_PUBLIC_BASE_URL}/blog/${slug}</guid> <description>${summary}</description> <pubDate>${new Date(publishedAt).toUTCString()}</pubDate> </item>`, ) .join("")} </channel> </rss>`; // 3. API作成 return new NextResponse(rss, { headers: { "Content-Type": "text/xml; charset=utf-8", "Cache-Control": "public, s-maxage=3600, stale-while-revalidate=86400", }, }); }

src/components/Header.tsx

import Image from "next/image"; import Link from "next/link"; import styles from "./Header.module.css"; export function Header() { return ( <header className={styles.header}> <div className={styles.container}> <nav className={styles.nav}> ... // リンクを追加 <Link href="/rss.xml" className={styles.item}> <Image src="/rss.svg" width={20} height={20} alt="RSS" /> </Link> </div> </nav> </div> </header> ); }

困ったところ

導入の際、苦戦したところはほとんどないですが、RSSのContent-Typeに関しては少しどうするべきかわからず困りました。

Content-Typeをapplication/rss+xmlにするのがRSSフィードであることを正確に示すものとして推奨されているため、最初こちらで実装しましたが、スマホだとブラウザによって挙動が異なりました。

  • safari: App Storeに飛ばそうとするダイアログを表示
  • chrome: rss.xmlをダウンロードしようとするダイアログが表示

この件は色々探しても詳しい記事が全く出なかったのでContent-Typeをapplication/rss+xmlにすることはやめて、Zennに合わせてtext/xmlに変更しました。こうすればどのブラウザでも問題なく/rss.xmlを表示することができました。

RSSフォーマットの確認

作ったRSSのフォーマットが問題ないかは、https://validator.w3.org/feed/ にレスポンスをそのまま投げて確認しました。

動作検証

導入したものが実際に動いているかどうかは、Feedlyという代表的なRSSリーダーにhttps://www.br-to.dev/rss.xml を登録して確認しました。

Feedlyは会員登録しないとフィードを確認できなかったので注意してください。

まとめ

以上でライブラリを使わずNext.jsのRoute Handlersを使ってRSSを導入することができました。引き続きブログ改善した

ここまで読んでいただき、ありがとうございました。

Next.js Route Handlers ライブラリなしでRSSフィードを作る