RSS Feed for Sveltekit 2.0
Sveltekit is ideal for building a blog or website that has dynamic content. RSS feeds are becoming more popular as internet users curate their own feeds rather than rely on algorithmic feeds from the social media giants.
Adding an RSS feed to sveltekit 2.0
Sveltekit is ideal for building a blog or website that has dynamic content. RSS feeds are becoming more popular as internet users curate their own feeds rather than rely on algorithmic feeds from the social media giants.
It's straightfoward to add a single additional route to your Sveltekit 2.0 project.
The standard place for RSS readers to look for an RSS feed is a file called rss.xml in the root directory. There are a few other alternatives than servers use such as feed.xml, rss.
So firstly you need to add a route in the root directory called /rss.xml and in it goes a single file called +server.js
This is all the code that is needed.
+server.js
import { hash, escapeHTML } from '$lib/utils.js';
export async function GET({locals, url}) {
const arrConfigs = await locals.pb.collection('blogConfig').getFullList( { } );
// find the config that matches our domain name.
let config;
arrConfigs.forEach(c=>{
if (c.meta.rssAllowed) {
let arrDomains = c.meta.domain.split(',');
let strMatch = arrDomains.find(strD=>strD.localeCompare(url.hostname)==0);
if (strMatch) config = c;
}
});
if ( ! config )
return new Response(`RSS Feed not found for ${url.hostname}`, {
headers: {
'Content-Type': 'application/text'
},
status: 404
});
let opts = {
filter:'blogName="'+config.blogName+'" && bCurrent=true && bPublished=true',
fields:'id,meta,slug', sort: '-updated'
};
const resultList = await locals.pb.collection('blogItem').getList( 1,20, opts );
// convert the raw data to a XML string
const body = render(url, config, resultList.items);
// see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
return new Response(body, {
headers: {
'Cache-Control': `max-age=${60*60*24} s-maxage=${60*60*24}`, // 1 day
'Content-Type': 'application/xml'
},
status: 200
});
};
function render(url, config,arrItems) {
return `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<atom:link href="${url.protocol}//${url.hostname}/rss" rel="self" type="application/rss+xml" />
<title>${config.meta.title}</title>
<link>${url.protocol}//${url.hostname}</link>
<description></description>
${arrItems
.map(
(item) => ` <item>
<guid>${hash(`${url.hostname}:${item.slug}:${item.updated}`)}</guid>
<title>${escapeHTML(item.meta.title)}</title>
<link>${url.protocol}//${url.hostname}/blog/posts/${item.slug}</link>
<description>${escapeHTML(item.meta.desc)}</description>
<pubDate>${new Date(item.meta.dtPublished).toISOString()}</pubDate>
</item>
`
)
.join('')}
</channel>
</rss>
`;
}
There are two functions. The first is essentially your code to retrieve your content from your DB, and the 2nd renders that content in the standard RSS format.
Both functions will need a little customisation to suit your data.
The two imported functions (hash, escapeHTML) are self explanatory.
The important part is that the file is named +server.js, and it contains the GET method that is called when the rss.xml file is requested. Fetching your items from your server to populate the RSS feed is pretty simple using Pocketbase. The code is run on the server only, because the database calls should be hidden from the client.
Once you have retrieved the items from your database, pass them to the rendering function which will return the complete text of the XML string that needs to be returned to the caller.
The final detail is that the Response should have a header that includes Content-Type: application/xml and optionally a Cache-Control Header. The body is just the text
Once it's implemented, test it by pointing your browser at localhost://rss.xml
Alternative Design
This implementation generates the XML string whenever any reader requests it. The function needs to access a couple of tables and potentially many rows of data for each request - one for each item. If this is a bit of a heavy load on your backend, then consider generating and storing the XML file on the server periodically. Maybe using CRON or maybe recreate it whenever a post is updated on your database The store & serve that XML from the above GET function.
Sitemap.xml
And adding a sitemap is mostly a cut & paste of the RSS.XML code above. Create a new route called /sitemap.xml and change the render() function to...
function render(url, config,arrItems) {
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${arrItems
.map(
(item) => ` <url>
<changefreq>monthly</changefreq>
<loc>${url.protocol}//${url.hostname}/blog/posts/${item.slug}</loc>
<lastmod>${new Date(item.updated).toISOString()}</lastmod>
</url>`
)
.join('')}
</urlset>
`;
}
- Published: 2024-02-09