TL;DR
Next.js と TypeScript を使った RSS の設定方法について解説しますね。非常に簡単に設定できます。
RSS(Really Simple Syndication)は、ウェブサイトの更新情報を一覧で知ることができるデータ形式の一つです。これを利用すると、ユーザーは RSS リーダーにウェブサイトの RSS フィードを登録することで、新規記事や更新記事の情報を一元的に取得できます。ユーザーエンゲージメントを高め、定期的な訪問者を増やすためのツールとして大変有用です。
Next.js で RSS を作成するには、サーバーサイドで動作する API エンドポイントを作成してそこから RSS を提供するのが一般的な方法です。次のような URL で RSS フィードを取得できます。
http://localhost:3000/api/feed
それでは、必要なライブラリをインストールします。
rss インストール
RSS フィードを生成するためにはrss
という名前のパッケージを使うことができます。以下のコマンドでインストールできます。
npm install rss
通常の設定
pages/api
ディレクトリ内に新しいファイルを作成します。例えば、rss.ts
という名前にすることができます。
このファイル内でrss
ライブラリを使用して RSS フィードを生成します。一般的には、次のようなコードになります。
Next/プロジェクト名/src/pages/api/feed.ts
import { NextApiRequest, NextApiResponse } from 'next'
import RSS from 'rss'
const fetchYourData = async () => {
return [
{
title: 'Example title 1',
description: 'Example description 1',
slug: 'example-slug-1',
author: 'Example author 1',
date: new Date(),
},
// More items...
]
}
const handler = async (_: NextApiRequest, res: NextApiResponse) => {
const feed = new RSS({
title: 'Your Website Title',
description: 'Your Website Description',
feed_url: 'https://yourwebsite.com/api/rss',
site_url: 'https://yourwebsite.com',
image_url: 'https://yourwebsite.com/icon.png',
managingEditor: 'Your Name',
webMaster: 'Your Name',
copyright: 'Your Copyright Notice',
language: 'ja',
pubDate: new Date().toString(),
ttl: 60,
})
const data = await fetchYourData()
data.forEach((item: { title: string; description: string; slug: string; author: string; date: Date }) => {
feed.item({
title: item.title,
description: item.description,
url: `https://yourwebsite.com/${item.slug}`,
author: item.author,
date: item.date,
})
})
res.setHeader('Content-Type', 'application/rss+xml')
res.write(feed.xml())
res.end()
}
export default handler
このスクリプトは API エンドポイントとして動作し、データソースから記事をフェッチしてそれを RSS フィードに追加しています。fetchYourData
関数はダミーデータの例であり、ここを自分のデータソースからデータを取得するように書き換えてください。
最後に、RSS XML をレスポンスとして返します。このエンドポイントはhttps://yourwebsite.com/api/rss
でアクセスできます。RSS リーダーはこの URL から RSS フィードを取得することができます。
注意点としては、サイトのタイトル、説明、URL、記事データなどを正しく設定することが重要です。また、フィード生成に時間がかかる場合は、キャッシュを使用することを検討してください。
ローカルで開発する場合
ローカルで開発中でも RSS フィードを確認することは可能です。Next.js の API ルーティングはローカル開発サーバでも動作します。
- まず、
npm run dev
などを使用して Next.js のローカル開発サーバを起動します。 - 次に、ブラウザを開き、作成した API エンドポイントの URL を指定します。例えば、API エンドポイントを
pages/api/rss.ts
に作成した場合、その URL はhttp://localhost:3000/api/rss
となります(3000 は Next.js のデフォルトのポートです。別のポートを設定した場合はそのポートを使用してください)。 - この URL にアクセスすると、作成した RSS フィードが表示されます。RSS フィードは XML 形式で表示されます。
RSS フィードをパースして人間が読みやすい形式で表示するためには、専用の RSS リーダーを使用することを推奨します。多くのブラウザは XML を構造化して表示しますが、RSS リーダーはフィードの各エントリーに対応する情報を適切に表示します。
Markdown からの設定
マークダウンファイルからのデータをフィードに反映させるには、まずそれらのマークダウンファイルを解析してデータを取得する必要があります。gray-matter というライブラリを使うことで、マークダウンファイルからメタデータ(フロントマター)と本文を抽出することができます。
それに加え、Next.js では getStaticProps という関数を使って、ビルド時に静的データを取得することができます。この関数の中でマークダウンファイルからデータを取得し、それをフィードに反映させることができます。
マークダウンが格納されている場所を、data/blog だとすると、次のような設定になります。ローカルでの例です。
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
import { NextApiRequest, NextApiResponse } from 'next'
import RSS from 'rss'
const POSTS_DIRECTORY = path.join(process.cwd(), 'data/blog')
const getAllPosts = () => {
// Get file names under /posts
const fileNames = fs.readdirSync(POSTS_DIRECTORY)
const allPostsData = fileNames.map((fileName) => {
// Remove ".md" from file name to get id
const id = fileName.replace(/\.md$/, '')
// Read markdown file as string
const fullPath = path.join(POSTS_DIRECTORY, fileName)
const fileContents = fs.readFileSync(fullPath, 'utf8')
// Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents)
// Combine the data with the id
return {
id,
...(matterResult.data as { date: string; title: string }),
}
})
return allPostsData.sort((a, b) => {
if (a.date < b.date) {
return 1
} else {
return -1
}
})
}
const handler = async (_: NextApiRequest, res: NextApiResponse) => {
const feed = new RSS({
title: 'Your Website Title',
description: 'Your Website Description',
feed_url: 'http://localhost:3000/feed',
site_url: 'http://localhost:3000',
image_url: 'http://localhost:3000/icon.png',
managingEditor: 'Your Name',
webMaster: 'Your Name',
copyright: 'Your Copyright Notice',
language: 'ja',
pubDate: new Date().toString(),
ttl: 60,
})
const data = getAllPosts()
data.forEach((item: { date: string; title: string; id: string }) => {
feed.item({
title: item.title,
description: '', // descriptionがないので、空文字列を渡します。
url: `http://localhost:3000/blog/${item.id}`, // slugはidと同じと仮定しています。
author: '', // authorがないので、空文字列を渡します。
date: new Date(item.date), // dateをDateオブジェクトに変換します。
})
})
res.setHeader('Content-Type', 'application/rss+xml; charset=utf-8')
res.write(feed.xml())
res.end()
}
export default handler