前回の記事までで、XM Cloud に対象となるアイテムのテンプレートを作成、サンプルのアイテムを GraphQL を利用して Json のデータで取得することができました。今回は後半で、RSS でデータが見れるようにしていきます。
- XM Cloud で管理している記事の RSS フィードを作成する(前編)
- XM Cloud で管理している記事の RSS フィードを作成する(後編)
Next.js 経由で GraphQL を呼び出す
まず GraphQL の Endpoint を利用してデータを取得するための汎用関数を用意します。このコードは少し長いため、以下の URL を参照してください。
ここでは fetchGraphQL が用意されており、動作は以下の通りです。
- fetchGraphQL(query: string) としており、query には GraphQL の文字列を指定します
- 以下の2つのキーを用意しています
- apiKey : evn.local に SITECORE_API_KEY の環境変数を指定する必要があります
- endpointUrl : evn.local に GraphQL のエンドポイントとして GRAPH_QL_ENDPOINT を指定します
- 上記2つのキーを利用して fetch で GraphQL エンドポイントに対して問い合わせ、その結果を返します
今回は .env.local に以下の定義を追加します。
GRAPH_QL_ENDPOINT=https://xmcloudcm.localhost/sitecore/api/graph/edge
SITECORE_API_KEY=1830F204-87EB-4132-9A7A-C9E4CD7C9A19
これで fetchGraphQL を利用することが可能となりました。
rss.xml の作成
今回は Next.js の App Router を利用するため、ルートのパスで rss.xml を返すことを想定して、以下のパスに route.ts ファイルを作成します。
- src\tailwindcss\src\app\rss.xml\route.ts
インターフェイスの作成
前回、最後に取得したデータをもとに返ってくる Json のデータを扱うための Interface をまず作成します。今回は以下のように作りました。
interface News {
id: string;
url: {
hostName: string;
path: string;
};
title: {
value: string;
};
publishDate: {
value: string;
};
description: {
value: string;
};
image: {
jsonValue: {
value: {
src: string;
alt: string;
width: string;
height: string;
};
};
};
}
interface AllNewsResponse {
data: {
search: {
total: number;
results: Partial<News>[];
};
};
}
Query の作成
前回動作した GraphQL のクエリに関して、パラメーターを渡して呼び出すために、以下のように作成をします。
const AllNewsQuery = (language: string, siteRootId: string, templateId: string) => {
return `
query {
search(
where: {
name: "_templates"
value: "${templateId}"
operator: EQ
AND: [
{
name: "_path"
value: "${siteRootId}"
operator: CONTAINS
}
{ name: "_language", value: "${language}", operator: EQ }
]
}
orderBy: { direction: DESC, name: "publishDate" }
) {
total
results {
id
url {
hostName
path
}
title: field(name: "Title") {
value
}
publishDate: field(name: "PublishDate") {
value
}
description: field(name: "Description") {
value
}
image: field(name: "Image") {
jsonValue
}
}
}
}
`;
};
これを呼び出す際には、言語、サイトのルートの ID および対象となるテンプレートの ID の値を利用して、必要となるデータを取得するためのクエリが生成されます。
データを取得する
これまで用意したコードを利用して、以下のようにデータを取得することができる関数を用意します。fetchGraphQL を呼び出しているため、import { fetchGraphQL } from 'src/utils'; のコードも追加してください。
async function getAllArticle(language: string, siteRootId: string, templateId: string) {
const results: AllNewsResponse = (await fetchGraphQL(
AllNewsQuery(language, siteRootId, templateId)
)) as AllNewsResponse;
const news: Partial<News>[] = [];
results.data.search.results.forEach((post: Partial<News>) => {
news.push({
id: post.id,
url: post.url,
title: post.title,
description: post.description,
publishDate: post.publishDate,
image: post.image,
});
});
return news;
}
GET 関数の作成
最後に、リクエストされた際にデータを返す GET 関数を作成します。今回は Json のデータを取得できているのかを確認するために、以下のようなコードをまず用意しました。
export async function GET() {
const posts = await getAllArticle(
'en',
'E66EE43B-398B-486E-9F7F-5FE36A4093D3',
'B9453B23-0E09-4D98-99C0-EAA0F16DD6DA'
);
return new Response(JSON.stringify({ posts }), {
headers: { 'Content-Type': 'application/json' },
});
}
この結果を確認するために、npm run start:connected のコマンドを実行したあと、http://localhost:3000/rss.xml にアクセスをするとデータが取れていることを確認できました。
データを加工する
取得したデータを利用して RSS 形式のデータで結果を表示するように route.ts ファイルを変更していきます。今回はパッケージとして、以下のパッケージを利用します。
以下のコマンドを実行してパッケージを追加します。
npm install feed
まず RSS が利用する URL を以下のように定義します。
const baseUrl = "https://sitecoredemo.jp";
続いて GET の中で、feed で必要となる情報を設定していきます。
const feed = new Feed({
title: 'Sitecoredemo.jp RSS',
description: 'This is RSS Feed about demo news',
id: baseUrl,
link: baseUrl,
copyright: `${new Date().getFullYear()} Sitecoredemo.jp`,
language: 'en',
favicon: baseUrl + 'favicon.png',
feedLinks: {
rss2: baseUrl + 'rss.xml',
},
author: {
name: 'Shinichi Haramizu',
email: 'support@sitecoredemo.jp',
link: baseUrl,
},
});
続いて、作成した feed に対して、取得したデータを追加していきます。
posts.map((post) => {
feed.addItem({
title: post.title?.value || 'Title',
id: post.id,
link: baseUrl + post.url?.path,
date: new Date(post.publishDate?.value || '2024-01-10'),
description: post.description?.value || '',
image: {
url: post.image?.jsonValue.value.src || '/next.svg',
},
});
});
最後に、生成された feed のデータをレスポンスとして返します。
return new Response(feed.rss2(), {
headers: {
'Content-Type': 'application/atom+xml; charset=utf-8',
},
});
実際に Postman を利用して http://localhost:3000/rss.xml にアクセスをすると、以下のようにデータの取得ができています。
日付の修正
Sitecore で管理している日付のデータは、以下のような文字列になっています。
- 20240730T150000Z
この結果、日付に関しては Invalid Date という結果が表示されています。これを修正するために、日付のデータに関して正規表現で扱えるように変換をします。
const publishDate = post.publishDate?.value || '20240110T00:00:00Z';
const isoDateString = publishDate.replace(
/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z/,
'$1-$2-$3T$4:$5:$6Z'
);
データを渡す部分を以下のように書き換えます。
date: new Date(isoDateString),
これで正しく日付も表示ができるようになりました。
Endpoint および API キーが無効の場合
これまでデータを利用して実行して、という形で問題なく動いていましたが、 GRAPH_QL_ENDPOINT および SITECORE_API_HOST が空の場合(初回 Build の状況)では Build エラーが発生してしまいます。これを回避するために、try - catch を追加して、Build の際にエラーが出ないように、処理が正しくできなかった場合はリダイレクト処理をするように、サンプルのコードを更新しました。
最終的なサンプルコードは以下の通りです。
まとめ
今回は RSS を実装するために、前編ではデータの取得、後編ではそのデータを利用して RSS のデータを加工する、という形で仕上げました。URL に関しては Next.js App Router を利用して作成しているため、今回はルートに rss.xml となっていますが、パスを切ったり、異なる RSS の場合はファイル名を変更することが可能です。ある程度汎用性を持たせるために、言語、テンプレートの ID そしてサイトのルート ID をパラメータとして呼び出すようにしてあります。