Parsing Feeds
Comprehensive guide to parsing RSS/Atom feeds with rss.Today.
Supported Formats
The Parser class automatically detects and parses:
- RSS 0.9 - Original RSS format
- RSS 1.0 - RDF-based RSS format
- RSS 2.0 - Most common RSS format
- Atom 1.0 - Atom syndication format
- JSON Feed 1.0 - JSON-based feed format
Input Sources
Parse from URL
Fetch and parse a feed from a URL:
import { Parser } from 'rss.today';
const parser = new Parser();
const feed = await parser.parseURL('https://example.com/feed.xml');Custom headers:
const parser = new Parser({
headers: {
'User-Agent': 'MyApp/1.0',
'Authorization': 'Bearer token'
}
});
const feed = await parser.parseURL('https://example.com/feed.xml');Parse from String
Parse XML from a string:
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 feed</description>
<item>
<title>First Item</title>
<link>https://example.com/first</link>
</item>
</channel>
</rss>`;
const feed = await parser.parseString(xml);Parse from File
Parse a local file:
const feed = await parser.parseFile('./feeds/my-feed.xml');Parse from Buffer
Parse from a Buffer:
const buffer = Buffer.from(xmlContent, 'utf-8');
const feed = await parser.parseBuffer(buffer);
// With custom encoding
const buffer = Buffer.from(xmlContent, 'utf-16');
const feed = await parser.parseBuffer(buffer, 'utf-16');Parse from Stream
Parse from a Node.js Readable stream:
import * as fs from 'fs';
const stream = fs.createReadStream('./feed.xml');
const feed = await parser.parseStream(stream);
// Custom chunk size limit
const feed = await parser.parseStream(stream, 5 * 1024 * 1024); // 5MBParse JSON Feed
Parse JSON Feed 1.0 format:
const jsonFeed = `{
"version": "https://jsonfeed.org/version/1",
"title": "Example Feed",
"home_page_url": "https://example.com",
"feed_url": "https://example.com/feed.json",
"items": [
{
"id": "1",
"title": "First Item",
"url": "https://example.com/first",
"date_published": "2024-01-01T00:00:00Z"
}
]
}`;
const feed = await parser.parseJSON(jsonFeed);Parsed Feed Structure
ParsedFeed Interface
interface ParsedFeed {
title?: string; // Feed title
description?: string; // Feed description
link?: string; // Website URL
feedUrl?: string; // Feed URL
language?: string; // Language code
lastBuildDate?: string; // Last build date
image?: { // Feed image
url?: string;
title?: string;
link?: string;
width?: string;
height?: string;
};
items: ParsedItem[]; // Feed items
paginationLinks?: Record<string, string>; // Pagination links
itunes?: any; // Podcast metadata (iTunes)
[key: string]: any; // Custom fields
}ParsedItem Interface
interface ParsedItem {
title?: string; // Item title
link?: string; // Item URL
pubDate?: string; // Publication date
date?: string; // Date (alternative field)
isoDate?: string; // ISO 8601 formatted date
author?: string; // Author name
content?: string; // Full HTML content
contentSnippet?: string; // Plain text content snippet
summary?: string; // Summary/alternative content
guid?: string | { // Global unique identifier
_: string;
$?: { isPermaLink?: string }
};
enclosure?: { // Media enclosure
url?: string;
length?: string;
type?: string;
};
categories?: any[]; // Categories
itunes?: any; // Podcast metadata
[key: string]: any; // Custom fields
}Custom Fields
Extract custom XML fields from feeds:
const parser = new Parser({
customFields: {
feed: ['dc:creator', 'dc:date', 'sy:updatePeriod'],
item: ['dc:subject', 'media:thumbnail', 'content:encoded']
}
});
const feed = await parser.parseURL('https://example.com/feed.xml');
// Access custom fields
console.log(feed['dc:creator']);
console.log(feed['dc:date']);
feed.items.forEach(item => {
console.log(item['dc:subject']);
console.log(item['media:thumbnail']);
});Podcast Support
The parser automatically extracts iTunes podcast metadata:
const feed = await parser.parseURL('https://example.com/podcast.xml');
// Feed-level iTunes metadata
console.log(feed.itunes?.author); // Podcast author
console.log(feed.itunes?.subtitle); // Short subtitle
console.log(feed.itunes?.summary); // Detailed summary
console.log(feed.itunes?.explicit); // Explicit content flag
console.log(feed.itunes?.keywords); // Keywords array
console.log(feed.itunes?.image); // Podcast image URL
console.log(feed.itunes?.owner); // Owner information
console.log(feed.itunes?.categories); // Categories with subcategories
// Item-level iTunes metadata
feed.items.forEach(item => {
console.log(item.itunes?.author); // Episode author
console.log(item.itunes?.subtitle); // Episode subtitle
console.log(item.itunes?.summary); // Episode summary
console.log(item.itunes?.duration); // Episode duration
console.log(item.itunes?.explicit); // Explicit content flag
console.log(item.itunes?.keywords); // Keywords
console.log(item.itunes?.image); // Episode image URL
});Format Detection
The parser automatically detects the feed format:
- Atom:
result.feedexists - RSS 2.0:
result.rss.$.versionstarts with “2” - RSS 1.0:
result["rdf:RDF"]exists - RSS 0.9:
result.rss.$.versionmatches “0.9”
Default RSS Version
For unversioned RSS feeds, specify a default version:
const parser = new Parser({
defaultRSS: 2 // Treat unversioned RSS as RSS 2.0
});Error Handling
Handle parsing errors gracefully:
import { Parser } from 'rss.today';
const parser = new Parser();
try {
const feed = await parser.parseURL('https://example.com/feed.xml');
console.log(feed.title);
} catch (error) {
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('Invalid XML')) {
console.error('Malformed XML');
} else {
console.error('Error:', error.message);
}
}Practical Examples
Fetch and Display Feed
import { Parser } from 'rss.today';
async function displayFeed(url: string, itemLimit: number = 10) {
const parser = new Parser();
const feed = await parser.parseURL(url);
console.log(`=== ${feed.title} ===`);
console.log(feed.description);
console.log(`Link: ${feed.link}`);
console.log(`Language: ${feed.language}`);
console.log(`Items: ${feed.items.length}\n`);
feed.items.slice(0, itemLimit).forEach((item, index) => {
console.log(`${index + 1}. ${item.title}`);
console.log(` ${item.link}`);
if (item.pubDate) {
console.log(` Published: ${item.pubDate}`);
}
if (item.author) {
console.log(` Author: ${item.author}`);
}
console.log();
});
}
displayFeed('https://example.com/feed.xml');Extract Podcast Episodes
import { Parser } from 'rss.today';
async function getPodcastEpisodes(feedUrl: string) {
const parser = new Parser();
const feed = await parser.parseURL(feedUrl);
if (!feed.itunes) {
console.log('Not a podcast feed');
return;
}
console.log(`Podcast: ${feed.title}`);
console.log(`Author: ${feed.itunes.author}`);
console.log(`Total Episodes: ${feed.items.length}\n`);
feed.items.forEach((item, index) => {
console.log(`Episode ${index + 1}: ${item.title}`);
console.log(`Duration: ${item.itunes?.duration || 'Unknown'}`);
console.log(`Published: ${item.pubDate}`);
console.log(`Audio: ${item.enclosure?.url}`);
console.log();
});
}
getPodcastEpisodes('https://example.com/podcast.xml');Parse with Custom Fields
import { Parser } from 'rss.today';
async function parseWithCustomFields(xml: string) {
const parser = new Parser({
customFields: {
feed: ['media:credit', 'media:copyright'],
item: ['media:content', 'media:thumbnail']
}
});
const feed = await parser.parseString(xml);
console.log('Custom feed fields:');
console.log(feed['media:credit']);
console.log(feed['media:copyright']);
feed.items.forEach(item => {
console.log(`Item: ${item.title}`);
console.log('Media content:', item['media:content']);
console.log('Thumbnail:', item['media:thumbnail']);
});
}Last updated on