Next.js と TypeScript で、RSS を出力する

TwitterFacebookHatena

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 ルーティングはローカル開発サーバでも動作します。

  1. まず、npm run devなどを使用して Next.js のローカル開発サーバを起動します。
  2. 次に、ブラウザを開き、作成した API エンドポイントの URL を指定します。例えば、API エンドポイントをpages/api/rss.tsに作成した場合、その URL はhttp://localhost:3000/api/rssとなります(3000 は Next.js のデフォルトのポートです。別のポートを設定した場合はそのポートを使用してください)。
  3. この 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

Next.js と TypeScript で、RSS を出力する