Up to the last article, we were able to create a template of the target item in XM Cloud and retrieve the sample item in Json data using GraphQL. In the second half of this article, we will make the data viewable via RSS.
- Create RSS feeds for articles managed by XM Cloud - Part 1
- Create RSS feeds for articles managed by XM Cloud - Part 2
Call GraphQL with Next.js
First, we need a generic function to retrieve data using GraphQL's Endpoint. This code is a bit long, so please refer to the following URL
Here fetchGraphQL is provided and works as follows.
- fetchGraphQL(query: string), where query is a GraphQL string
- The following two keys are available
- apiKey : need to specify SITECORE_API_KEY environment variable in evn.local
- endpointUrl : GRAPH_QL_ENDPOINT as GraphQL endpoint in evn.local
- Using the above two keys, fetch queries a GraphQL endpoint and returns the results
This time, add the following definition to .env.local
GRAPH_QL_ENDPOINT=https://xmcloudcm.localhost/sitecore/api/graph/edge
SITECORE_API_KEY=1830F204-87EB-4132-9A7A-C9E4CD7C9A19
Now you can use fetchGraphQL.
Create rss.xml
Since we will be using Next.js' App Router, we will create a route.ts file in the following path, assuming that rss.xml will be returned in the route path.
- src\tailwindcss\src\app\rss.xml\route.ts
Creating Interfaces
The first step is to create an Interface to handle the Json data returned based on the last data obtained in the previous step. This time, we created the following 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>[];
};
};
}
Creating a Query
For the GraphQL query that worked last time, we create the following to call it passing parameters
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
}
}
}
}
`;
};
When this is called, a query is generated to retrieve the needed data using the values of the language, the ID of the site root and the ID of the target template.
Retrieve data
Using the code prepared so far, prepare a function that can retrieve data as follows: Since we are calling fetchGraphQL, please also add the code 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;
}
Creating GET Functions
Finally, we create a GET function that returns data when requested. In this case, we first prepared the following code to check whether Json data was obtained.
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' },
});
}
To confirm this result, after running the command npm run start:connected, I accessed http://localhost:3000/rss.xml and was able to confirm that the data was retrieved.
Process data
We will modify the route.ts file to display the results in RSS format using the acquired data. This time, we will use the following package.
Execute the following command to add the package
npm install feed
First, define the URLs to be used by RSS as follows
const baseUrl = "https://sitecoredemo.jp";
Next, in the GET, we will set up the information required by the 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,
},
});
Next, the acquired data is added to the created 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',
},
});
});
Finally, the generated feed data is returned as a response.
return new Response(feed.rss2(), {
headers: {
'Content-Type': 'application/atom+xml; charset=utf-8',
},
});
In fact, when I access http://localhost:3000/rss.xml using Postman, I am able to get data as followsす。
Convert Date format
The date data managed by Sitecore is the following string.
- 20240730T150000Z
As a result, Invalid Date is displayed for dates. To correct this, the date data is converted to be handled by a regular expression.
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'
);
Rewrite the part that passes data as follows
date: new Date(isoDateString),
The date can now be displayed correctly.
Endpoint and API keys are invalid
So far, it has been working fine using data and executing, but when GRAPH_QL_ENDPOINT and SITECORE_API_HOST are empty (first time Build situation), a Build error occurs. To work around this, we have updated the sample code by adding try - catch to avoid errors during Build and redirect processing if it fails to do so correctly.
The final sample code is as follows
Summary
In order to implement RSS, the first part of this project was to acquire data, and the second part was to process RSS data using that data. However, it is possible to cut the path or change the file name in case of different RSS. To make it somewhat generic, the language, template ID, and site root ID are called as parameters.