SMASHINGMAGAZINE.COM
Build A Static RSS Reader To Fight Your Inner FOMO
In a fast-paced industry like tech, it can be hard to deal with the fear of missing out on important news. But, as many of us know, theres an absolutely huge amount of information coming in daily, and finding the right time and balance to keep up can be difficult, if not stressful. A classic piece of technology like an RSS feed is a delightful way of taking back ownership of our own time. In this article, we will create a static Really Simple Syndication (RSS) reader that will bring you the latest curated news only once (yes: once) a day.Well obviously work with RSS technology in the process, but were also going to combine it with some things that maybe you havent tried before, including Astro (the static site framework), TypeScript (for JavaScript goodies), a package called rss-parser (for connecting things together), as well as scheduled functions and build hooks provided by Netlify (although there are other services that do this).I chose these technologies purely because I really, really enjoy them! There may be other solutions out there that are more performant, come with more features, or are simply more comfortable to you and in those cases, I encourage you to swap in whatever youd like. The most important thing is getting the end result!The PlanHeres how this will go. Astro generates the website. I made the intentional decision to use a static site because I want the different RSS feeds to be fetched only once during build time, and thats something we can control each time the site is rebuilt and redeployed with updates. Thats where Netlifys scheduled functions come into play, as they let us trigger rebuilds automatically at specific times. There is no need to manually check for updates and deploy them! Cron jobs can just as readily do this if you prefer a server-side solution.During the triggered rebuild, well let the rss-parser package do exactly what it says it does: parse a list of RSS feeds that are contained in an array. The package also allows us to set a filter for the fetched results so that we only get ones from the past day, week, and so on. Personally, I only render the news from the last seven days to prevent content overload. Well get there!But first...What Is RSS?RSS is a web feed technology that you can feed into a reader or news aggregator. Because RSS is standardized, you know what to expect when it comes to the feeds format. That means we have a ton of fun possibilities when it comes to handling the data that the feed provides. Most news websites have their own RSS feed that you can subscribe to (this is Smashing Magazines RSS feed: https://www.smashingmagazine.com/feed/). An RSS feed is capable of updating every time a site publishes new content, which means it can be a quick source of the latest news, but we can tailor that frequency as well.RSS feeds are written in an Extensible Markup Language (XML) format and have specific elements that can be used within it. Instead of focusing too much on the technicalities here, Ill give you a link to the RSS specification. Dont worry; that page should be scannable enough for you to find the most pertinent information you need, like the kinds of elements that are supported and what they represent. For this tutorial, were only using the following elements: <title>, <link>, <description>, <item>, and <pubDate>. Well also let our RSS parser package do some of the work for us.Creating The State SiteWell start by creating our Astro site! In your terminal run pnpm create astro@latest. You can use any package manager you want Im simply trying out pnpm for myself.After running the command, Astros chat-based helper, Houston, walks through some setup questions to get things started. astro Launch sequence initiated. dir Where should we create your new project? ./rss-buddy tmpl How would you like to start your new project? Include sample files ts Do you plan to write TypeScript? Yes use How strict should TypeScript be? Strict deps Install dependencies? Yes git Initialize a new git repository? YesI like to use Astros sample files so I can get started quickly, but were going to clean them up a bit in the process. Lets clean up the src/pages/index.astro file by removing everything inside of the <main></main> tags. Then were good to go!From there, we can spin things by running pnpm start. Your terminal will tell you which localhost address you can find your site at.Pulling Information From RSS feedsThe src/pages/index.astro file is where we will make an array of RSS feeds we want to follow. We will be using Astros template syntax, so between the two code fences (---), create an array of feedSources and add some feeds. If you need inspiration, you can copy this:const feedSources = [ 'https://www.smashingmagazine.com/feed/', 'https://developer.mozilla.org/en-US/blog/rss.xml', // etc.]Now well install the rss-parser package in our project by running pnpm install rss-parser. This package is a small library that turns the XML that we get from fetching an RSS feed into JavaScript objects. This makes it easy for us to read our RSS feeds and manipulate the data any way we want.Once the package is installed, open the src/pages/index.astro file, and at the top, well import the rss-parser and instantiate the Partner class.import Parser from 'rss-parser';const parser = new Parser();We use this parser to read our RSS feeds and (surprise!) parse them to JavaScript. Were going to be dealing with a list of promises here. Normally, I would probably use Promise.all(), but the thing is, this is supposed to be a complicated experience. If one of the feeds doesnt work for some reason, Id prefer to simply ignore it.Why? Well, because Promise.all() rejects everything even if only one of its promises is rejected. That might mean that if one feed doesnt behave the way Id expect it to, my entire page would be blank when I grab my hot beverage to read the news in the morning. I do not want to start my day confronted by an error.Instead, Ill opt to use Promise.allSettled(). This method will actually let all promises complete even if one of them fails. In our case, this means any feed that errors will just be ignored, which is perfect.Lets add this to the src/pages/index.astro file:interface FeedItem { feed?: string; title?: string; link?: string; date?: Date;}const feedItems: FeedItem[] = [];await Promise.allSettled( feedSources.map(async (source) => { try { const feed = await parser.parseURL(source); feed.items.forEach((item) => { const date = item.pubDate ? new Date(item.pubDate) : undefined; feedItems.push({ feed: feed.title, title: item.title, link: item.link, date, }); }); } catch (error) { console.error(Error fetching feed from ${source}:, error); } }));This creates an array (or more) named feedItems. For each URL in the feedSources array we created earlier, the rss-parser retrieves the items and, yes, parses them into JavaScript. Then, we return whatever data we want! Well keep it simple for now and only return the following:The feed title,The title of the feed item,The link to the item,And the items published date.The next step is to ensure that all items are sorted by date so well truly get the latest news. Add this small piece of code to our work:const sortedFeedItems = feedItems.sort((a, b) => (b.date ?? new Date()).getTime() - (a.date ?? new Date()).getTime());Oh, and... remember when I said I didnt want this RSS reader to render anything older than seven days? Lets tackle that right now since were already in this code.Well make a new variable called sevenDaysAgo and assign it a date. Well then set that date to seven days ago and use that logic before we add a new item to our feedItems array.This is what the src/pages/index.astro file should now look like at this point:---import Layout from '../layouts/Layout.astro';import Parser from 'rss-parser';const parser = new Parser();const sevenDaysAgo = new Date();sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);const feedSources = [ 'https://www.smashingmagazine.com/feed/', 'https://developer.mozilla.org/en-US/blog/rss.xml',]interface FeedItem { feed?: string; title?: string; link?: string; date?: Date;}const feedItems: FeedItem[] = [];await Promise.allSettled( feedSources.map(async (source) => { try { const feed = await parser.parseURL(source); feed.items.forEach((item) => { const date = item.pubDate ? new Date(item.pubDate) : undefined; if (date && date >= sevenDaysAgo) { feedItems.push({ feed: feed.title, title: item.title, link: item.link, date, }); } }); } catch (error) { console.error(Error fetching feed from ${source}:, error); } }));const sortedFeedItems = feedItems.sort((a, b) => (b.date ?? new Date()).getTime() - (a.date ?? new Date()).getTime());---<Layout title="Welcome to Astro."> <main> </main></Layout>Rendering XML DataIts time to show our news articles on the Astro site! To keep this simple, well format the items in an unordered list rather than some other fancy layout.All we need to do is update the <Layout> element in the file with the XML objects sprinkled in for a feed items title, URL, and publish date.<Layout title="Welcome to Astro."> <main> {sortedFeedItems.map(item => ( <ul> <li> <a href={item.link}>{item.title}</a> <p>{item.feed}</p> <p>{item.date}</p> </li> </ul> ))} </main></Layout>Go ahead and run pnpm start from the terminal. The page should display an unordered list of feed items. Of course, everything is styled at the moment, but luckily for you, you can make it look exactly like you want with CSS!And remember that there are even more fields available in the XML for each item if you want to display more information. If you run the following snippet in your DevTools console, youll see all of the fields you have at your disposal:feed.items.forEach(item => {}Scheduling Daily Static Site BuildsWere nearly done! The feeds are being fetched, and they are returning data back to us in JavaScript for use in our Astro page template. Since feeds are updated whenever new content is published, we need a way to fetch the latest items from it.We want to avoid doing any of this manually. So, lets set this site on Netlify to gain access to their scheduled functions that trigger a rebuild and their build hooks that do the building. Again, other services do this, and youre welcome to roll this work with another provider Im just partial to Netlify since I work there. In any case, you can follow Netlifys documentation for setting up a new site. Once your site is hosted and live, you are ready to schedule your rebuilds. A build hook gives you a URL to use to trigger the new build, looking something like this:https://api.netlify.com/build_hooks/your-build-hook-idLets trigger builds every day at midnight. Well use Netlifys scheduled functions. Thats really why Im using Netlify to host this in the first place. Having them at the ready via the host greatly simplifies things since theres no server work or complicated configurations to get this going. Set it and forget it!Well install @netlify/functions (instructions) to the project and then create the following file in the projects root directory: netlify/functions/deploy.ts.This is what we want to add to that file:// netlify/functions/deploy.tsimport type { Config } from '@netlify/functions';const BUILD_HOOK = 'https://api.netlify.com/build_hooks/your-build-hook-id'; // replace me!export default async (req: Request) => { await fetch(BUILD_HOOK, { method: 'POST', }).then((response) => { console.log('Build hook response:', response.json()); }); return { statusCode: 200, };};export const config: Config = { schedule: '0 0 * * *',};If you commit your code and push it, your site should re-deploy automatically. From that point on, it follows a schedule that rebuilds the site every day at midnight, ready for you to take your morning brew and catch up on everything that you think is important.
0 Comments
0 Shares
221 Views