Examples
Complete usage examples for rss.Today.
Table of Contents
- Basic Parsing
- Feed Generation
- Podcast Feed
- Feed Aggregation
- Feed Monitoring
- Format Conversion
- OPML Operations
- Feed Filtering
- Validation
- Error Handling
Basic Parsing
Parse from URL
import { Parser } from 'rss.today';
async function parseFromURL() {
const parser = new Parser();
const feed = await parser.parseURL('https://example.com/feed.xml');
console.log('Feed:', feed.title);
console.log('Description:', feed.description);
console.log('Items:', feed.items.length);
feed.items.forEach(item => {
console.log(`- ${item.title}`);
console.log(` ${item.link}`);
});
}
parseFromURL();Parse from String
import { Parser } from 'rss.today';
async function parseFromString() {
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>Example Feed</title>
<link>https://example.com</link>
<description>An example RSS feed</description>
<item>
<title>First Item</title>
<link>https://example.com/first</link>
<description>Description of first item</description>
</item>
</channel>
</rss>`;
const parser = new Parser();
const feed = await parser.parseString(xml);
console.log(feed.title);
console.log(feed.items[0].title);
}
parseFromString();Parse from File
import { Parser } from 'rss.today';
import * as fs from 'fs/promises';
async function parseFromFile() {
const parser = new Parser();
const feed = await parser.parseFile('./feeds/my-feed.xml');
console.log(`Loaded feed: ${feed.title}`);
console.log(`Total items: ${feed.items.length}`);
}
parseFromFile();Feed Generation
Basic RSS Feed
import { Feed } from 'rss.today';
const feed = new Feed({
id: 'https://example.com/feed',
title: 'My Blog',
description: 'A blog about technology',
link: 'https://example.com',
copyright: '2024 My Blog',
language: 'en'
});
feed.addItem({
title: 'First Post',
link: 'https://example.com/first-post',
description: 'This is my first post',
date: new Date()
});
feed.addItem({
title: 'Second Post',
link: 'https://example.com/second-post',
description: 'This is my second post',
date: new Date()
});
const rss = feed.rss2();
console.log(rss);Generate Multiple Formats
import { Feed } from 'rss.today';
const feed = new Feed({
id: 'https://example.com/feed',
title: 'My Blog',
link: 'https://example.com',
copyright: '2024 My Blog'
});
feed.addItem({
title: 'Post Title',
link: 'https://example.com/post',
description: 'Post description',
date: new Date()
});
// Generate all formats
const rss2 = feed.rss2();
const atom1 = feed.atom1();
const rss1 = feed.rss1();
const rss09 = feed.rss0_9();
const json1 = feed.json1();
console.log('RSS 2.0:', rss2);
console.log('Atom 1.0:', atom1);
console.log('JSON Feed:', json1);Blog Feed with Categories and Authors
import { Feed } from 'rss.today';
const feed = new Feed({
id: 'https://blog.example.com/feed',
title: 'Tech Blog',
description: 'Technology and programming articles',
link: 'https://blog.example.com',
copyright: '2024 Tech Blog',
language: 'en',
updated: new Date(),
author: {
name: 'Blog Author',
email: 'author@blog.example.com',
link: 'https://blog.example.com/author'
}
});
// Add categories
feed.addCategory('Technology');
feed.addCategory('Programming');
feed.addCategory('Web Development');
// Add contributors
feed.addContributor({
name: 'Jane Doe',
email: 'jane@example.com'
});
feed.addContributor({
name: 'John Smith',
email: 'john@example.com'
});
// Add posts
feed.addItem({
title: 'Introduction to TypeScript',
link: 'https://blog.example.com/typescript-intro',
description: 'Learn TypeScript basics',
content: '<p>TypeScript is a typed superset of JavaScript...</p>',
date: new Date('2024-01-15'),
author: [{
name: 'Jane Doe',
email: 'jane@example.com'
}],
category: [
{ name: 'TypeScript' },
{ name: 'JavaScript' }
]
});
feed.addItem({
title: 'React Hooks Guide',
link: 'https://blog.example.com/react-hooks',
description: 'Master React Hooks',
content: '<p>React Hooks are functions that let you...</p>',
date: new Date('2024-01-20'),
author: [{
name: 'John Smith',
email: 'john@example.com'
}],
category: [
{ name: 'React' },
{ name: 'Frontend' }
]
});
const rss = feed.rss2();
console.log(rss);Podcast Feed
Complete Podcast Feed
import { Feed } from 'rss.today';
const feed = new Feed({
id: 'https://podcast.example.com/feed',
title: 'Tech Talk Podcast',
description: 'Weekly discussions about technology',
link: 'https://podcast.example.com',
feed: 'https://podcast.example.com/feed.xml',
copyright: '2024 Tech Talk',
language: 'en',
updated: new Date(),
podcast: true,
category: 'Technology',
author: {
name: 'Tech Talk Host',
email: 'host@podcast.example.com'
},
image: 'https://podcast.example.com/podcast-cover.jpg'
});
// Add iTunes metadata
feed.addExtension({
name: 'itunes',
objects: {
author: 'Tech Talk Host',
subtitle: 'Weekly tech discussions',
summary: 'Join us every week for discussions about the latest in technology.',
explicit: 'clean',
keywords: ['technology', 'programming', 'software'],
image: 'https://podcast.example.com/podcast-cover.jpg',
owner: {
name: 'Tech Talk Host',
email: 'host@podcast.example.com'
},
categories: [
'Technology',
'Software How-To'
]
}
});
// Add episodes
feed.addItem({
title: 'Episode 1: Introduction to React',
link: 'https://podcast.example.com/episode-1',
description: 'Learn the basics of React framework',
date: new Date('2024-01-01'),
enclosure: {
url: 'https://podcast.example.com/episode-1.mp3',
type: 'audio/mpeg',
length: 15728640 // 15MB
}
});
feed.addItem({
title: 'Episode 2: TypeScript Deep Dive',
link: 'https://podcast.example.com/episode-2',
description: 'Advanced TypeScript techniques',
date: new Date('2024-01-08'),
enclosure: {
url: 'https://podcast.example.com/episode-2.mp3',
type: 'audio/mpeg',
length: 17825792 // 17MB
}
});
feed.addItem({
title: 'Episode 3: Node.js Best Practices',
link: 'https://podcast.example.com/episode-3',
description: 'Best practices for Node.js development',
date: new Date('2024-01-15'),
enclosure: {
url: 'https://podcast.example.com/episode-3.mp3',
type: 'audio/mpeg',
length: 19922944 // 19MB
}
});
const rss = feed.rss2();
console.log(rss);Feed Aggregation
Merge Multiple Feeds
import { Parser, mergeFeeds, sortByDate, limitFeed } from 'rss.today';
async function aggregateFeeds(feedUrls: string[], limit: number = 20) {
const parser = new Parser();
console.log('Fetching feeds...');
// Fetch all feeds
const feeds = await Promise.all(
feedUrls.map(async (url) => {
try {
return await parser.parseURL(url);
} catch (error) {
console.error(`Failed to fetch ${url}:`, error.message);
return null;
}
})
);
// Filter out failed requests
const validFeeds = feeds.filter((feed): feed is NonNullable<typeof feed> => feed !== null);
console.log(`Successfully fetched ${validFeeds.length} feeds`);
// Merge feeds
const merged = mergeFeeds(validFeeds);
console.log(`Merged ${merged.items.length} items`);
// Sort by date (newest first)
const sorted = sortByDate(merged, 'desc');
// Limit results
const final = limitFeed(sorted, limit);
console.log(`\n=== Aggregated Feed ===`);
console.log(`Total items: ${final.items.length}\n`);
final.items.forEach((item, index) => {
console.log(`${index + 1}. ${item.title}`);
console.log(` ${item.link}`);
if (item.pubDate) {
console.log(` ${item.pubDate}`);
}
console.log();
});
return final;
}
aggregateFeeds([
'https://techcrunch.com/feed/',
'https://arstechnica.com/feed/',
'https://www.theverge.com/rss/index.xml'
], 10);Feed Monitoring
Detect Feed Changes
import { Parser, diffFeeds } from 'rss.today';
import * as fs from 'fs/promises';
async function monitorFeed(url: string) {
const parser = new Parser();
// Load previous state
let oldFeed = null;
try {
const saved = await fs.readFile('./feed-state.json', 'utf-8');
oldFeed = JSON.parse(saved);
} catch {
console.log('No previous state found, starting fresh');
}
// Fetch current feed
console.log(`Fetching: ${url}`);
const newFeed = await parser.parseURL(url);
if (oldFeed) {
console.log(`\nOld feed: ${oldFeed.items.length} items`);
console.log(`New feed: ${newFeed.items.length} items\n`);
// Compare feeds
const diff = diffFeeds(oldFeed, newFeed);
if (diff.added.length > 0) {
console.log(`✨ New items (${diff.added.length}):`);
diff.added.forEach(item => {
console.log(` + ${item.title}`);
});
console.log();
}
if (diff.removed.length > 0) {
console.log(`🗑️ Removed items (${diff.removed.length}):`);
diff.removed.forEach(item => {
console.log(` - ${item.title}`);
});
console.log();
}
if (diff.modified.length > 0) {
console.log(`✏️ Modified items (${diff.modified.length}):`);
diff.modified.forEach(item => {
console.log(` ~ ${item.title}`);
});
console.log();
}
} else {
console.log(`\nInitial feed: ${newFeed.items.length} items\n`);
newFeed.items.slice(0, 5).forEach(item => {
console.log(` ${item.title}`);
});
}
// Save current state
await fs.writeFile('./feed-state.json', JSON.stringify(newFeed, null, 2));
console.log('State saved');
}
monitorFeed('https://example.com/feed.xml');Format Conversion
Convert Between Formats
import { Parser, convertToRSS2, convertToAtom, convertToJSON } from 'rss.today';
import * as fs from 'fs/promises';
async function convertFeedFormats(sourceUrl: string, outputDir: string) {
const parser = new Parser();
console.log(`Fetching: ${sourceUrl}`);
const feed = await parser.parseURL(sourceUrl);
console.log(`Title: ${feed.title}`);
console.log(`Items: ${feed.items.length}\n`);
// Convert to all formats
console.log('Converting to RSS 2.0...');
const rss2 = convertToRSS2(feed);
await fs.writeFile(`${outputDir}/feed.rss.xml`, rss2);
console.log('Converting to Atom 1.0...');
const atom = convertToAtom(feed);
await fs.writeFile(`${outputDir}/feed.atom.xml`, atom);
console.log('Converting to JSON Feed...');
const json = convertToJSON(feed);
await fs.writeFile(`${outputDir}/feed.json`, json);
console.log('\nAll formats converted successfully!');
console.log(` - RSS 2.0: ${outputDir}/feed.rss.xml`);
console.log(` - Atom 1.0: ${outputDir}/feed.atom.xml`);
console.log(` - JSON Feed: ${outputDir}/feed.json`);
}
convertFeedFormats('https://example.com/feed.xml', './output');OPML Operations
Parse OPML and Import Feeds
import { OPMLParser, Parser } from 'rss.today';
import * as fs from 'fs/promises';
async function importFromOPML(opmlPath: string) {
// Parse OPML file
const xml = await fs.readFile(opmlPath, 'utf-8');
const opmlParser = new OPMLParser();
const opml = await opmlParser.parseOPML(xml);
console.log(`=== ${opml.title} ===`);
console.log(`Total feeds: ${countFeeds(opml.outlines)}\n`);
// Parse each feed
const feedParser = new Parser();
let successCount = 0;
let failCount = 0;
for (const outline of opml.outlines) {
if (outline.type === 'rss' && outline.xmlUrl) {
try {
const feed = await feedParser.parseURL(outline.xmlUrl);
console.log(`✓ ${outline.text}`);
console.log(` ${feed.title}`);
console.log(` Items: ${feed.items.length}\n`);
successCount++;
} catch (error: any) {
console.log(`✗ ${outline.text}`);
console.log(` Error: ${error.message}\n`);
failCount++;
}
} else if (outline.outlines) {
for (const feed of outline.outlines) {
if (feed.type === 'rss' && feed.xmlUrl) {
try {
const parsed = await feedParser.parseURL(feed.xmlUrl);
console.log(`✓ ${feed.text}`);
console.log(` ${parsed.title}\n`);
successCount++;
} catch (error: any) {
console.log(`✗ ${feed.text}`);
console.log(` Error: ${error.message}\n`);
failCount++;
}
}
}
}
}
console.log(`\nSummary: ${successCount} succeeded, ${failCount} failed`);
}
function countFeeds(outlines: any[]): number {
let count = 0;
for (const outline of outlines) {
if (outline.type === 'rss') {
count++;
} else if (outline.outlines) {
count += countFeeds(outline.outlines);
}
}
return count;
}
importFromOPML('./subscriptions.opml');Export Feeds to OPML
import { Parser, OPMLGenerator, type OPMLDocument } from 'rss.today';
import * as fs from 'fs/promises';
async function exportToOPML(feedUrls: string[], outputPath: string) {
const parser = new Parser();
const outlines = [];
console.log('Fetching feeds...\n');
for (const url of feedUrls) {
try {
const feed = await parser.parseURL(url);
outlines.push({
text: feed.title || url,
type: 'rss',
xmlUrl: url,
htmlUrl: feed.link,
description: feed.description
});
console.log(`✓ ${feed.title}`);
} catch (error: any) {
console.log(`✗ ${url}`);
console.log(` Error: ${error.message}`);
}
}
const opml: OPMLDocument = {
title: 'Exported Subscriptions',
dateCreated: new Date(),
dateModified: new Date(),
ownerName: 'Your Name',
ownerEmail: 'your@example.com',
outlines
};
const generator = new OPMLGenerator();
const opmlXml = generator.generateOPML(opml);
await fs.writeFile(outputPath, opmlXml);
console.log(`\nExported ${outlines.length} feeds to ${outputPath}`);
}
exportToOPML(
[
'https://techcrunch.com/feed/',
'https://arstechnica.com/feed/',
'https://www.theverge.com/rss/index.xml'
],
'./exports/subscriptions.opml'
);Feed Filtering
Filter by Category
import { Parser, filterByCategory } from 'rss.today';
async function getTechPosts(url: string) {
const parser = new Parser();
const feed = await parser.parseURL(url);
const techFeed = filterByCategory(feed, 'technology');
console.log(`Found ${techFeed.items.length} technology posts`);
techFeed.items.forEach(item => {
console.log(`- ${item.title}`);
});
}
getTechPosts('https://example.com/feed.xml');Filter by Date Range
import { Parser, filterByDateRange, sortByDate, limitFeed } from 'rss.today';
async function getRecentPosts(url: string, days: number = 7) {
const parser = new Parser();
const feed = await parser.parseURL(url);
// Filter by date range
const startDate = new Date();
startDate.setDate(startDate.getDate() - days);
const recentFeed = filterByDateRange(feed, startDate);
// Sort by date
const sorted = sortByDate(recentFeed, 'desc');
// Limit results
const results = limitFeed(sorted, 10);
console.log(`Posts from last ${days} days:`);
results.items.forEach(item => {
console.log(`- ${item.title}`);
console.log(` ${item.pubDate}`);
});
}
getRecentPosts('https://example.com/feed.xml', 7);Validation
Validate RSS 2.0 Feed
import { Parser, validateRSS2 } from 'rss.today';
async function validateRSSFeed(url: string) {
const parser = new Parser();
console.log(`Validating: ${url}\n`);
const feed = await parser.parseURL(url);
const result = validateRSS2(feed);
if (result.valid) {
console.log('✅ Feed is valid RSS 2.0');
} else {
console.log('❌ Feed has errors:');
result.errors.forEach(error => console.log(` - ${error}`));
}
if (result.warnings.length > 0) {
console.log('\n⚠️ Warnings:');
result.warnings.forEach(warning => console.log(` - ${warning}`));
}
return result.valid;
}
validateRSSFeed('https://example.com/feed.xml');Validate Before Processing
import { Parser, validateRSS2, validateAtom } from 'rss.today';
async function safeParse(url: string) {
const parser = new Parser();
const feed = await parser.parseURL(url);
// Try RSS 2.0 validation
const rss2Result = validateRSS2(feed);
if (rss2Result.valid) {
console.log('Valid RSS 2.0 feed');
return feed;
}
// Try Atom validation
const atomResult = validateAtom(feed);
if (atomResult.valid) {
console.log('Valid Atom feed');
return feed;
}
console.log('Feed validation failed');
console.log('RSS 2.0 errors:', rss2Result.errors);
console.log('Atom errors:', atomResult.errors);
}
safeParse('https://example.com/feed.xml');Error Handling
Comprehensive Error Handling
import { Parser } from 'rss.today';
async function robustParse(url: string) {
const parser = new Parser({
timeout: 30000,
maxRedirects: 5
});
try {
const feed = await parser.parseURL(url);
if (!feed || !feed.items) {
console.log('Empty or invalid feed');
return null;
}
console.log(`Success: ${feed.title}`);
console.log(`Items: ${feed.items.length}`);
return feed;
} catch (error: any) {
if (error.message.includes('Feed not recognized')) {
console.error('❌ Unsupported feed format');
} else if (error.message.includes('Status code')) {
console.error(`❌ HTTP error: ${error.message}`);
} else if (error.message.includes('File not found')) {
console.error('❌ File does not exist');
} else if (error.message.includes('Unable to parse XML')) {
console.error('❌ Malformed XML');
} else if (error.message.includes('timeout')) {
console.error('❌ Request timeout');
} else {
console.error('❌ Unknown error:', error.message);
}
return null;
}
}
robustParse('https://example.com/feed.xml');Retry Logic
import { Parser } from 'rss.today';
async function parseWithRetry(url: string, maxRetries: number = 3) {
const parser = new Parser();
let lastError: Error | null = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
console.log(`Attempt ${attempt}/${maxRetries}...`);
const feed = await parser.parseURL(url);
console.log('Success!');
return feed;
} catch (error: any) {
lastError = error;
console.log(`Failed: ${error.message}`);
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
console.log(`Waiting ${delay}ms before retry...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
console.error(`Failed after ${maxRetries} attempts`);
throw lastError;
}
parseWithRetry('https://example.com/feed.xml');Last updated on