A comprehensive TypeScript package for generating RSS and Atom feeds in Node.js applications.
Framework-agnostic with built-in caching support, rich media features, and 100% type safety.
This package is part of a comprehensive feed generation suite created by the same author:
- π PHP: RumenDamyanov/php-feed - Composer package
- πΉ Go: RumenDamyanov/go-feed - Go module
- π¨ Node.js: This package - NPM package for JavaScript/TypeScript
All implementations share the same core philosophy and feature set with language-specific optimizations.
- π― Full TypeScript Support - Complete type safety and IntelliSense
- ποΈ Modern Architecture - ESM/CJS dual package with tree-shaking support
- π Framework Agnostic - Works with Express, Next.js, Fastify, or any Node.js project
- π‘ Multiple Formats - RSS 2.0 and Atom 1.0 support
- β‘ High Performance - Optimized for large feeds with efficient memory usage
- π Advanced Validation - Built-in URL validation and data sanitization
- π Rich Media Support - Images, videos, translations, and Google News
- π¨ Custom Views - Template-based feed generation with framework adapters
- π§ Dependency Injection - Clean architecture with adapter pattern
- π Internationalization - Full multi-language and translation support
- π§ͺ Battle Tested - Comprehensive test coverage with real-world validation
- π¦ Zero Dependencies - Minimal runtime footprint
# npm
npm install @rumenx/feed
# yarn
yarn add @rumenx/feed
# pnpm
pnpm add @rumenx/feedimport { Feed } from '@rumenx/feed';
// Create a new feed
const feed = new Feed();
// Configure the feed
feed
.setTitle('My Blog Feed')
.setDescription('Latest posts from my blog')
.setLink('https://example.com')
.setLanguage('en');
// Add items
feed
.addItem({
title: 'First Post',
description: 'This is my first blog post',
link: 'https://example.com/posts/first-post',
author: 'Rumen Damyanov',
pubdate: new Date(),
})
.addItem({
title: 'Second Post',
description: 'Another interesting post',
link: 'https://example.com/posts/second-post',
author: 'Rumen Damyanov',
pubdate: new Date(),
images: [
{
url: 'https://example.com/images/post-image.jpg',
caption: 'Post featured image',
},
],
});
// Generate RSS feed
const rssXml = feed.toXML('rss');
console.log(rssXml);
// Generate Atom feed
const atomXml = feed.toXML('atom');
console.log(atomXml);import express from 'express';
import { FeedFactory } from '@rumenx/feed';
const app = express();
app.get('/feed.xml', (req, res) => {
const feed = FeedFactory.createForExpress({
baseUrl: 'https://example.com',
validate: true,
});
feed.setTitle('My Blog').setDescription('Latest blog posts').setLink('https://example.com');
// Add your content
feed.addItem({
title: 'Hello World',
description: 'My first post',
link: 'https://example.com/hello',
pubdate: new Date(),
});
// Render and send response
const xml = feed.render('rss');
res.set('Content-Type', 'application/xml');
res.send(xml);
});
app.listen(3000);// pages/api/feed.xml.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { FeedFactory } from '@rumenx/feed';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const feed = FeedFactory.createForNextjs({
baseUrl: 'https://yoursite.com',
});
feed.setTitle('My Next.js Blog').setDescription('Latest posts').setLink('https://yoursite.com');
// Add your posts
feed.addItem({
title: 'Next.js is Awesome',
description: 'Why I love Next.js',
link: 'https://yoursite.com/posts/nextjs-awesome',
pubdate: new Date(),
});
res.setHeader('Content-Type', 'application/xml');
res.status(200).send(feed.toXML('rss'));
}import { Feed } from '@rumenx/feed';
const feed = new Feed({
baseUrl: 'https://example.com',
validate: true,
escapeContent: true,
});
// Add item with images and videos
feed.addItem({
title: 'Amazing Travel Guide',
description: 'Discover the best travel destinations',
link: 'https://example.com/travel-guide',
author: 'Travel Blogger',
pubdate: new Date(),
images: [
{
url: 'https://example.com/images/travel.jpg',
caption: 'Beautiful mountain landscape',
title: 'Mountain View',
license: 'https://creativecommons.org/licenses/by/4.0/',
geoLocation: 'Swiss Alps',
},
],
videos: [
{
thumbnail_url: 'https://example.com/thumbnails/travel-video.jpg',
title: 'Travel Video Guide',
description: 'A comprehensive video guide to travel',
content_url: 'https://example.com/videos/travel.mp4',
duration: 300,
rating: 4.8,
view_count: 15000,
publication_date: '2023-12-01',
family_friendly: true,
tags: ['travel', 'guide', 'adventure'],
},
],
});// Add multilingual content with translations
feed.addItem({
title: 'Global News Update',
description: 'Latest international news',
link: 'https://example.com/news/global-update',
translations: [
{ language: 'en', url: 'https://example.com/en/news/global-update' },
{ language: 'es', url: 'https://example.com/es/noticias/actualizacion-global' },
{ language: 'fr', url: 'https://example.com/fr/nouvelles/mise-a-jour-mondiale' },
{ language: 'de', url: 'https://example.com/de/nachrichten/globale-aktualisierung' },
],
});// Add Google News compatible items
feed.addItem({
title: 'Breaking: Major Discovery in Science',
description: 'Scientists make groundbreaking discovery',
link: 'https://example.com/news/major-discovery',
pubdate: new Date(),
news: {
sitename: 'Science Daily',
language: 'en',
publication_date: new Date(),
title: 'Major Discovery in Quantum Physics',
keywords: 'science, quantum, physics, discovery',
},
});interface FeedConfig {
/** Base URL for resolving relative URLs */
baseUrl?: string;
/** Enable validation of URLs and data */
validate?: boolean;
/** Enable XML content escaping */
escapeContent?: boolean;
/** Pretty print XML output */
prettyPrint?: boolean;
/** Date format string */
dateFormat?: string;
/** Feed language */
language?: string;
/** Maximum items per feed */
maxItems?: number;
/** Maximum file size in bytes */
maxFileSize?: number;
/** Allowed domains for URL validation */
allowedDomains?: string[];
}// Get detailed feed statistics
const stats = feed.getStats();
console.log(stats);
// Output:
// {
// totalItems: 25,
// totalImages: 12,
// totalVideos: 3,
// totalTranslations: 75,
// averagePriority: 0.8,
// lastModified: '2023-12-01T10:30:00.000Z',
// sizeEstimate: 145000
// }
// Check if feed should be split
if (feed.shouldSplit()) {
console.log('Feed is too large and should be split into multiple feeds');
}import { ValidationError } from '@rumenx/feed';
// Validate feed content
const errors = feed.validate();
if (errors.length > 0) {
console.error('Feed validation errors:', errors);
}
// Handle validation during configuration
const feed = new Feed({
validate: true, // Throws errors on invalid data
});
try {
feed.addItem({
title: 'Invalid Item',
description: 'This item has invalid data',
link: 'not-a-valid-url', // Will throw validation error
});
} catch (error) {
if (error instanceof ValidationError) {
console.error('Validation error:', {
field: error.field,
type: error.type,
message: error.message,
value: error.value,
});
}
}This package is written in TypeScript and provides complete type definitions:
import type {
Feed,
FeedConfig,
FeedItem,
FeedItemData,
FeedStats,
ValidationError,
ImageData,
VideoData,
NewsData,
} from '@rumenx/feed';
// Full IntelliSense support
const item: FeedItemData = {
title: 'My Post',
description: 'Post description',
link: 'https://example.com/post',
pubdate: new Date(),
images: [
/* Fully typed image objects */
],
videos: [
/* Fully typed video objects */
],
};- Optimized for large feeds - Efficiently handles 50,000+ items
- Memory efficient - Stream-friendly architecture for large datasets
- Fast XML generation - Optimized string building and concatenation
- Minimal dependencies - Zero runtime dependencies for optimal bundle size
- Tree-shakable - Import only what you need with modern bundlers
- Node.js: >= 18.0.0
- TypeScript: >= 4.5.0 (for TypeScript projects)
Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
This project is licensed under the MIT License - see the LICENSE.md file for details.
If you find this package helpful, please consider:
- β Starring this repository
- π Reporting bugs and suggesting improvements
- π» Contributing code or documentation
- π Sponsoring the project
Built with β€οΈ by Rumen Damyanov