a-blog cms × Next.js で RSS Feed を実装する

公開日:

目次

a-blog cms をバックエンドで動かして、Next.js でフロントエンドを構築している Web サイトで RSS Feed を実装する方法を紹介します。

このサイトでも今回紹介する方法で実装した RSS Feed を以下のURLで配信しています。是非、ご利用の RSS Reader に登録してみてください。

https://blog.uidev.jp/feed

利用するライブラリ

次のライブラリを利用します。npm などでインストールしてください。

Feed_Rss2 モジュールと Ogp モジュールのモジュールIDを作成する

RSS Feed の作成に必要なデータを取得するためのモジュールIDを作成します。作成するモジュールIDは以下の2つです。

  • Feed_Rss2
  • Ogp

それぞれのモジュールIDを API 機能を有効にして作成してください。

Feed_Rss2 モジュールのモジュールID名を feed 、Ogp モジュールのモジュールID名を ogp とします。

Route Handlers で RSS Feed を配信するAPIを実装する

Next.js の Route Handlers という機能を活用して、RSS Feed を配信するAPIを実装します。

app/feed/route.ts を作成し、次のようなコードを実装します。

import { Feed } from 'feed';
import { getOgp } from '../api';
import { getRssFeed } from './api';

const BASE_URL = 'https://example.com'; // WebサイトのURL
const MEDIA_BASE_URL = 'https://assets.example.com/media' // アセット配信のURL

function truncate(
  string: string,
  length: number,
  end: string = '...',
) {
  return [...string].length <= length
    ? string
    : `${string.substring(0, length)}${end}`;
}

function deleteNewLine(string: string): string {
  return string.replace(/\r?\n/g, '');
}

export async function GET() {
  const [ogp, rss] = await Promise.all([getOgp(), getRssFeed()]);

  const feed = new Feed({
    title: ogp.title,
    description: deleteNewLine(truncate(ogp.description, 350)),
    id: BASE_URL,
    link: BASE_URL,
    image: ogp.image?.path
      ? new URL(ogp.image.path, MEDIA_BASE_URL).toString()
      : undefined,
    copyright: '© 2023  All Rights Reserved.',
    updated: rss.lastBuildDate,
    generator: 'a-blog cms',
    author: {
      name: 'authorName',
      email: 'author@example.com',
      link: 'https://example.com/profile/',
    },
  });

  rss.items.forEach((item) => {
    feed.addItem({
      title: item.title,
      id: item.link,
      link: item.link,
      ...(item.category ? { category: [{ name: item.category }] } : {}),
      author: [
        {
          name: item.creator,
          email: 'author@example.com',
          link: 'https://example.com/profile/',
        },
      ],
      date: item.pubDate,
    });
  });

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

feed のドキュメント通りに値を設定します。

また、レスポンスヘッダーで Content-Typeapplication/xml; charset=utf-8 に設定しています。

acms-js-sdk を初期化する

acms-js-sdk は app/lib/acms/index.ts で初期化してインポートして利用できるようにします。

// app/lib/acms/index.ts

import { createClient } from '@uidev1116/acms-js-sdk';

const acmsClient = createClient({
  baseUrl: YOUR_BASE_URL,
  apiKey: YOUR_API_KEY,
});

export default acmsClient;

Feed_Rss2 モジュールのデータを取得する

次に、Feed_Rss2 モジュールのデータを取得するための getRssFeed 関数を実装します。

import acmsClient from '@/app/lib/acms';
import { AcmsContext } from '@uidev1116/acms-js-sdk/acmsPath';

const BASE_URL = 'https://example.com'; // WebサイトのURL

export type RssFeedItem = {
  title: string;
  link: string;
  pubDate: Date;
  category: string;
  permalink: string;
  creator: string;
};

export type RssFeed = {
  items: RssFeedItem[];
  lastBuildDate: Date;
};

export async function getRssFeed(
  acmsContext: AcmsContext = {},
): Promise<RssFeed> {
  const { data } = await acmsClient.get(
    { ...acmsContext, api: 'feed' },
  );
  const { 'item:loop': items = [], lastBuildDate } = data;
  return {
    items: items.map((item: any) => ({
      title: item.title,
      link: new URL(new URL(item.link).pathname, BASE_URL).toString(),
      pubDate: new Date(item.pubDate),
      category: item.category,
      permalink: new URL(new URL(item.permalink).pathname, BASE_URL).toString(),
      creator: item.creator,
    })),
    lastBuildDate: new Date(lastBuildDate),
  };
}

OGP モジュールのデータを取得する

次に、Ogp モジュールのデータを取得するための getOgp 関数を実装します。

import acmsClient from '@/app/lib/acms';
import { AcmsContext } from '@uidev1116/acms-js-sdk/acmsPath';

const BASE_URL = 'https://example.com'; // WebサイトのURL

type Ogp = {
title: string;
description: string;
keywords: string;
image?: {
  path: string;
  width: number;
  height: number;
};
};

export async function getOgp(acmsContext: AcmsContext = {}): Promise<Ogp> {
const { data } = await acmsClient.get(
  { ...acmsContext, api: 'ogp' }
);

const {
  title = '',
  description = '',
  keywords = '',
  image: imagePath = '',
  'image@x': imageWidth = 0,
  'image@y': imageHeight = 0,
} = data;

return {
  title,
  description,
  keywords,
  ...(imagePath !== ''
    ? {
        image: {
          path: imagePath,
          width: imageWidth,
          height: imageHeight,
        },
      }
    : {}),
};
}

これで、a-blog cms のデータを使って RSS Feed を実装することができるようになります。

まとめ

今回は feed というライブラリを活用して、a-blog cms をバックエンドで動かして、Next.js でフロントエンドを構築している Web サイトで RSS Feed を実装する方法を紹介しました。

今回の実装のデメリットとして、記事毎の description が設定できていません。Feed_Rss2 モジュールから記事の要約文を取得できたらよいのですが、現状のモジュールでは取得できません。

Entry_Summary などを組み合わせることで 記事毎の description を RSS Feed で配信することができますので余裕がある方は試してみてください。