One of my 2023 resolutions was to get back into the habit of reading, especially since I decided to stop being a snob and read whatever I truly wanted (even if that was some questionable fanfic-y literature). My go-to website to track the books I delve into is Goodreads. Goodreads has an amazing “news” section that’s essentially a blog where they recommend upcoming books and I usually align pretty well with what they promote over there, but the sad news is that they don’t offer an RSS feed for their news section.

Ever since I left (most) social media, a lot of the information I consume is through RSS feeds (thanks NetNewsWire), so this was an issue. Yes, I could’ve signed up for email notifications but in all reality I wanted to have all the content centralized in my reader, so that’s when I took it into my own hands to offer myself a way to create a custom RSS feed.


Before we start, make sure you have Node.js installed on your machine. If not, you can download it from Node.js’ website.

Step 1: Set Up the Project

Create a new Node.js project and install the dependencies we’ll be using.

mkdir goodreads-rss-feed
cd goodreads-rss-feed
npm init -y
npm install express request cheerio rss fs path

Step 2: Write an Express App

Write an Express App to expose an API endpoint to serve the XML output. Create a file named app.js and add the initial setup for your Express application.

const express = require('express');
const request = require('request');
const cheerio = require('cheerio');
const RSS = require('rss');
const fs = require('fs');
const path = require('path');

const app = express();
const port = process.env.PORT || 3000;

Step 3: Function to Create RSS Feed

Define a function createRSSFeed that will create the RSS feed by scraping Goodreads content. IMPORTANT: this approach could be used for virtually any website that has a sort-of defined HTML structure to offer a list of latest blog posts.

function createRSSFeed(url, feedTitle, feedDescription) {
  return new Promise((resolve, reject) => {
    request(url, (error, response, html) => {
      if (error || response.statusCode !== 200) {
        reject(error || `Request failed with status code ${response.statusCode}`);

      const $ = cheerio.load(html);
      const feed = new RSS({
        title: feedTitle,
        description: feedDescription,
        feed_url: `${url}/rss.xml`,
        site_url: url,

      // This chunk of code is based on Goodreads current HTML structure to show their latest blog posts
      // If you're using this for a different website, you'll need to inspect their markup and adjust this accordingly
      $('.editorialCard').each((index, element) => {
        const title = $(element).find('.editorialCard__title').text();
        const link = $(element).find('.editorialCard__title .gr-hyperlink').attr('href');
        const description = $(element).find('.editorialCard__body').text();
        const pubDate = $(element).find('.editorialCard__timestamp').text();

          title: title,
          description: description,
          url: link,
          date: new Date(pubDate),
      const xml = feed.xml({ indent: true });

Step 4: Express Route for RSS Feed

Define an Express route /api/rss that triggers the creation of the RSS feed when accessed and serves an XML output for your reader of choice to feed upon. IMPORTANT: I decided to use Vercel to host this API, which is why my route is preceded by /api, but this isn’t mandatory and could be adjusted based on how you’ll be hosting the application.

app.get('/api/rss', async (req, res) => {
  try {
    const feed = await createRSSFeed(
      'Goodreads RSS Feed',
      'Latest articles from Goodreads'

    res.header('Content-Type', 'application/xml');
  } catch (error) {
    console.error('Error generating or sending RSS feed:', error);
    res.status(500).send('Internal Server Error');

Step 5: Start the Server

Start the Express server to make your RSS feed accessible.

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);

module.exports = app;

Step 6: Run the Application

Run your application using the following command:

node app.js

IMPORTANT: for persistence, you could host this in a service such as Heroku or Vercel, rather than depending on the host machine running the application for availability.

Visit http://localhost:3000/api/rss in your browser to access the custom Goodreads RSS feed.

That’s it! You’ve successfully created a custom RSS feed for Goodreads using Node.js. You can now integrate this feed into your preferred RSS reader to stay updated with the latest content from Goodreads in a more personalized way.

NOTE: Since Goodreads doesn’t offer a full date on the posts they list on their website, and the markup doesn’t include a full date somewhere either, posts usually show up with weird dates. Similar issues may happen if other data is missing or is incomplete in the website you’re looking to create a feed for.