🪚

Next.js の API Routes を使って XML を JSON で返却したい

2024/01/26に公開

XML のフィードを Next.js 内のアプリケーションで利用する、みたいな要件があったので、実装した時のメモになります。

開発環境

  • macOS SONOMA 14.0 (Intel Mac)
  • node 18.17.0[1]
  • npm 9.6.7

Next.jsのバージョンとか

  • Next.js v13.4.12
  • React v18.2.0
  • TypeScript v5.1.6

XMLtoJSON に必要なパッケージ

開発時(2024年1月)、最後のアップデートが6ヶ月前で週間DL数やGitHubのスターが多かったので下記を採用しました。

https://www.npmjs.com/package/xml2js

https://www.npmjs.com/package/@types/xml2js

インストール

デフォルトだとTypeScriptに対応していないっぽいので型情報も入れておきます。
Next.jsのインストールとかは省きますのでご了承ください。

$ npm install xml2js
$ npm install --save @types/xml2js

DEV Communityのフィード(XML)をJSONで返却する

諸事情で Pages Router の API Routes を使います。
せっかく App Router 使えるバージョンなのに、なんで Route Handlers じゃないんだというツッコミは勘弁でお願いします。

サンプルコード

src/pages/api/feeds.ts
import type { NextApiRequest, NextApiResponse } from "next";

import { Parser } from "xml2js";

interface ChannelItem {
  title: string;
  link: string;
  pubDate: string;
  "dc:creator": string;
  category: string[];
  guid: string;
  description: string;
}

interface XmlResponse {
  rss: {
    // xml の属性は $ でアクセスできる
    $: {
      version: string;
      "xmlns:dc": string;
      "xmlns:atom": string;
    };
    channel: {
      title: string;
      "atom:link": {
        $: {
          href: string;
          rel: string;
          type: string;
        };
      };
      link: string;
      description: string;
      language: string;
      item: ChannelItem[];
    };
  };
}

const feedUrl = "https://dev.to/feed";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<{ data: ChannelItem[] }>
) {
  const response = await fetch(feedUrl);

  const parser = new Parser({
    // true にすると、それぞれの値が配列になってしまうので false にしておく
    explicitArray: false,
    // 前後の空句を取り除く
    trim: true,
  });

  // xml を string としてに変換
  const xmlString = await response.text();

  // xml を json に変換 generics型を使えないので as で型をキャスト
  const data = await parser.parseStringPromise(xmlString) as XmlResponse;

  // item を json で返却
  res.status(200).json({ data: data.rss.channel.item });
}

返却値

ローカル環境を立ち上げて、下記URLを叩いて次のようなJSONが返ってくればOK。

{
  "data": [
    {
      "title": "記事タイトル",
      "dc:creator": "書いた人の名前",
      "pubDate": "公開日時が入っている",
      "link": "記事詳細のリンクが入ってる",
      "guid": "記事詳細のリンクが入ってる",
      "description": "記事詳細のhtmlが入ってる",
      "category": ["カテゴリ1", "カテゴリ2", "カテゴリ3", "カテゴリ4"]
    },
    // 以下略
  ]
}

感想

JSON色付けするお仕事は頻繁にあるけど、XMLをJSONにして色付けするお仕事は今回が初めてな記憶だったので勉強になりました。

おしまい。

追記(2024/01/29)

今思えばServerComponent側で処理すれば良いので、わざわざ API Routes 使う必要ないよね。

脚注
  1. 執筆時点のLTSは 20.11.0 ですがご了承ください ↩︎

株式会社HAMWORKS

Discussion