How to make an RSS feed in SvelteKit
Tags: svelte, sveltekit, javascript, tutorial
Published: 2021-06-14 20:50:33

---
canonical_url: https://www.davidwparker.com/posts/how-to-make-an-rss-feed-in-sveltekit
---

Chances are, if you're consuming a _lot_ of content, you're not checking a ton of individual sites.
You may be checking something like Reddit, or another aggregator, or possibly one of the bigger blogging platforms nowadays (dev.to, medium, etc). But that still leaves out large portions of the Internet.

If you control your own website and channel, and you're using SvelteKit, then you'll likely want an RSS feed so that your end users can subscribe to your content in their favorite feed reader.

So, what's it take to to it with SvelteKit? Not a lot!

Note: if you'd rather watch a video tutorial on how to implement an RSS feed, you can check out my [YouTube video here](https://www.youtube.com/watch?v=pJnvu7aC3e0).

Here's the complete code for this blog's rss feed:

`routes/rss.js`

```javascript
export const get = async () => {
  const res = await fetch(import.meta.env.VITE_BASE_ENDPOINT + '/posts/posts.json');
  const data = await res.json();
  const body = render(data.posts);
  const headers = {
    'Cache-Control': `max-age=0, s-max-age=${600}`,
    'Content-Type': 'application/xml',
  };
  return {
    body,
    headers,
  };
};

const render = (posts) => `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<atom:link href="http://wwww.davidwparker.com/rss" rel="self" type="application/rss+xml" />
<title>David W Parker</title>
<link>https://www.davidwparker.com</link>
<description>David W Parker's blog about Code, Entrepreneurship, and more</description>
${posts
  .map(
    (post) => `<item>
<guid>https://www.davidwparker.com/posts/${post.slug}</guid>
<title>${post.title}</title>
<link>https://www.davidwparker.com/posts/${post.slug}</link>
<description>${post.description}</description>
<pubDate>${new Date(post.published).toUTCString()}</pubDate>
</item>`
  )
  .join('')}
</channel>
</rss>
`;
```

## Let's break it down

### The endpoint

```javascript
// GET /rss
export const get = async () => {
  const res = await fetch(import.meta.env.VITE_BASE_ENDPOINT + '/posts/posts.json');
  const data = await res.json();
  const body = render(data.posts);
  const headers = {
    'Cache-Control': `max-age=0, s-max-age=${600}`,
    'Content-Type': 'application/xml',
  };
  return {
    body,
    headers,
  };
};
```

This is a `get` request that lives at `/rss`. In it, I make a simple request to `/posts/posts.json` to get all the blog
articles that I want for this RSS feed.
I call `res.json()` to get the resulting json, then send the posts within that json to the `render` method to build my body.
Once I get the body, I set a few headers, and return the resulting body and header which is needed for the SvelteKit endpoint.

### The body

```javascript
const render = (posts) => `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<atom:link href="http://wwww.davidwparker.com/rss" rel="self" type="application/rss+xml" />
<title>David W Parker</title>
<link>https://www.davidwparker.com</link>
<description>David W Parker's blog about Code, Entrepreneurship, and more</description>
${posts
  .map(
    (post) => `<item>
<guid>https://www.davidwparker.com/posts/${post.slug}</guid>
<title>${post.title}</title>
<link>https://www.davidwparker.com/posts/${post.slug}</link>
<description>${post.description}</description>
<pubDate>${new Date(post.published).toUTCString()}</pubDate>
</item>`
  )
  .join('')}
</channel>
</rss>
`;
```

We start by making our xml declaration and using the proper `rss` tag with the definition from w3.org.
From there, it's just a standard `rss` feed, which you can find from anywhere on the Internet.

In my example, I have a `channel`, with `atom:link` which references itself. Inside, I have a title for my feed/site, and a description. From there, I map each of my resulting posts into their own `<item>` tag along with their own `guid`, `title`, `link`, `description,` and `pubDate`. Close out the tags, and we're done.

### posts.json

This is less important, but it's just another `get` endpoint that returns a bunch of posts from imported `md` files.
At this point, there's a bunch of examples of this all around the Internet- but here's mine just in case you haven't seen it yet:

```javascript
// GET /posts/posts.json
export const get = async ({ query }) => {
  let posts = await Promise.all(
    Object.entries(import.meta.glob('./*.md')).map(async ([path, page]) => {
      const { metadata } = await page();
      const slug = path.split('/').pop().split('.').shift();
      return { ...metadata, slug };
    })
  );
  if (query.get('q') !== null) {
    posts = posts.reduce((accum, val) => {
      if (val.categories.includes(query.get('q'))) {
        accum.push(val);
      }
      return accum;
    }, []);
  }
  posts.sort((a, b) => (a.published > b.published ? -1 : 1));

  return {
    status: 200,
    body: { posts },
  };
};
```