rss-feed-emitter polls feeds and emits an event per new item
Super RSS News Feed aggregator written in Node.js and ES6
At a glance
- What is it?
- rss-feed-emitter is an MIT licensed Node.js module that polls RSS and Atom feeds on a refresh interval, tracks which items it has already seen, and emits a new-item event for anything new, with per feed event names, a skipFirstLoad option and a user agent override. It is also presented as a complete tutorial on shipping an npm module with tests, coverage and CI.
- Who is it for?
- rss-feed-emitter fits a Node.js service that needs to react to new entries in a set of feeds, since the polling, the deduplication of already seen items and the event emission are handled for you and the interface is four methods. It does not fit a project that needs a real time feed reader with conditional GET support, since the module polls on a timer, and it does not fit a codebase that has moved off the deprecated request library style dependency set.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The module is also a worked example of npm publishing
The README presents rss-feed-emitter in two roles at once, and the second one is unusual enough to be worth knowing about before you read the code.
It is described as a fully functional module, but its source code and repository are also a super complete tutorial, covering what to do first when creating a module from scratch, how to manage the module in GitHub and npm, how to create automated unit and integration tests, how to integrate them with Travis CI and make the build break if tests did not pass, how to automatically test the module against various versions of Node.js, how to set up a code coverage tool and keep 100% coverage, how to integrate the coverage results with Coveralls, how to configure linting tools, and how to deploy to GitHub and npm with tags and releases.
The README is direct about the audience for that. If you are afraid to read the source code of the modules you use or to create your first module, this is the best chance you have to break this barrier, and the entry point is an issue in the tracker rather than a document.
One line in that list is struck through, and it is a small history lesson. How to transpile your ES6 code into ES5 is marked as gone in v3, which tells you the module moved from shipping transpiled output to shipping ES6 directly once the supported Node.js floor moved past the versions that needed it.
So the repository has two audiences. The first is a developer who wants to poll some feeds. The second is a developer who wants to see a small module taken from an empty directory to a published npm package with a passing build, a coverage report and a release. The second audience arguably gets more out of it.
Adding a feed and listening for what is new
The interface is a constructor, an add method and an event. There is no configuration object to learn and no plugin surface.
const RssFeedEmitter = require('rss-feed-emitter');
const feeder = new RssFeedEmitter();A feed is added with a url and a refresh interval in milliseconds.
feeder.add({
url: 'http://www.nintendolife.com/feeds/news',
refresh: 2000
});The default refresh value is 60 seconds, which is worth noting because a two second interval in the example is aggressive for most feeds and most servers. You can add several feeds in one call either by giving the url field an array of addresses, in which case they share one refresh value, or by passing multiple config objects, each with its own refresh.
The event you listen for is new-item, and the payload is the parsed item.
feeder.on('new-item', function(item) {
console.log(item);
});The event name is overridable per feed, which is the detail that makes this usable for more than one feed.
feeder.add({
url: 'http://www.nintendolife.com/feeds/news',
refresh: 2000,
eventName: 'nintendo'
});
feeder.on('nintendo', function(item) {
console.log(item);
});That is per feed event routing. A consumer can listen for new-item to catch everything, or for a named event to catch one source, and the two coexist on the same emitter.
The history handling is automatic, which the feature list describes as automatically manages feed history memory. The module remembers what it has already emitted so a repeat poll does not re-emit the same entries, and that is the difference between a poll loop and an aggregator.
skipFirstLoad decides whether the backlog is data or noise
One option deserves its own attention, because it changes the meaning of the first event you receive.
const feeder = new RssFeedEmitter({ skipFirstLoad: true });
feeder.add({
url: 'http://www.nintendolife.com/feeds/news',
refresh: 2000,
eventName: 'nintendo'
});
// this item will only be from the new items, not from old items.
feeder.on('nintendo', function(item) {
console.log(item);
});Without the option, the first poll emits everything the feed currently holds. That is usually what you want when you are building a feed reader or a dashboard, because the existing entries are the content. With skipFirstLoad set, the first poll is treated as a baseline: the items are recorded in the history and no events fire, and only items that appear after that point are emitted.
That is the right behaviour for a notification service or a bot that should announce what is published next rather than replay the archive. It is the wrong behaviour for a site that renders a feed, because the initial page would be empty.
The other constructor option is the user agent, which controls the request header sent to the feed server.
const feeder = new RssFeedEmitter({ userAgent: 'Your UA string' });This matters more than it looks. Feed servers block default and missing user agents, and a feed that works in a browser can fail silently in a polling service. Setting your own string is also the polite thing to do, since it identifies the client.
The rest of the surface is lifecycle management. feeder.list is an ES6 getter that returns the feeds in the instance, feeder.remove takes a feed url to drop one of them, and feeder.destroy removes all feeds from the instance. The README describes the getter as being made an ES6 getter to make the field a bit more plain to access, which is a small example of the v3 rewrite.
Errors are events, and the default handler is your responsibility
The README includes a short note about error handling that is easy to skim and important in practice.
feeder.on('error', console.error);The comment explains why: handle error events by printing to console, and this handler is needed to prevent unhandled exceptions from crashing the process.
That is a real behaviour of Node's EventEmitter, where an error event with no listener is thrown. The module emits errors on the emitter, so a consumer that does not attach a handler will get an unhandled error event, and depending on the error that means the process exits. For a long running service that polls dozens of feeds over a network, an error event is a routine occurrence rather than an exceptional one, and the first thing a feed begins to do is return a 500 or time out.
So attaching an error handler is not optional for a production use, and the README says so in as many words.
The other thing the error path interacts with is the refresh schedule. A feed that fails has to be retried on the next cycle, and the module's automatic history management means a failed poll does not mark items as seen, so a feed that recovers does not lose the entries it published while it was down. The README does not spell out the retry semantics, so the exact behaviour on a failure is worth confirming in the source if it matters for your use case.
The dependency set is from the request era
The package.json is short and tells you a lot.
"dependencies": {
"feedparser": "1.1.4",
"request": "^2.88.2"
},
"engines": {
"node": ">=8.16.2"
}Two runtime dependencies. feedparser does the parsing of both RSS and Atom, which the keyword list confirms, and request does the HTTP. The request library is the one to think about. It was the standard HTTP client for Node.js for most of the 2010s and it is now in maintenance mode, and a project that depends on it inherits a transitive dependency tree that is no longer receiving new versions.
The engine floor is Node.js 8.16.2 or newer, and the feature list spells out the history. It supports Node.js 8.x up through the current version of Node.js, Node.js 4.x through 7.x were supported until version 2.0.1, and Node.js 0.10.x and 0.12.x until version 1.0.7. The floor has moved over the life of the package, and it has moved conservatively, which is a deliberate choice for a library with a wide install base.
The main field points at src/FeedEmitter.js, so the published package ships source rather than a build output, and the types field points at types/FeedEmitter.d.ts, with a build:types script that runs tsc against tsconfig.declaration.json to generate the declarations. The prepublishOnly script runs that build, so the type definitions are always regenerated before publishing.
The dev dependencies show the test stack: mocha as the runner, chai 3.4.0 for assertions, nock for HTTP mocking, nyc for coverage, and eslint with the airbnb-base config. The .mocharc.json and .nycrc.json files hold their configuration, and the .nvmrc pins a Node version for local development.
Where rss-feed-emitter is the wrong tool
The first wrong case is a scale requirement. The module polls on a timer, one request per feed per interval, with no conditional GET. There is no ETag handling and no Last-Modified handling, so every poll re-downloads the full feed even when nothing has changed. For a handful of feeds that is fine. For a few thousand feeds on a two minute interval, that is a lot of redundant bandwidth and a lot of load on someone else's server, and you are the polite problem rather than the rude one.
The second wrong case is a real time reader. Server-sent events or WebSockets give you the push model, and rss-feed-emitter is a pull model with a timer. If your product needs a subscriber to see an item within a second of publication, the refresh interval is your latency floor, and setting it to one second for a hundred feeds is not a good trade.
The third wrong case is a security review that objects to request. The dependency is the standard HTTP client of its generation and it is no longer the actively developed choice, so a team with a policy on unmaintained transitive dependencies will have to look at the tree before adopting. The alternative is to write a small wrapper, which is real work for a feature the module does well.
The fourth wrong case is expecting the current release to match the source. The package.json says version 3.0.0 while the tagged releases listed are v3.2.2 on 2020-08-13, v3.2.3 on 2022-02-12 and v3.2.4 on 2025-11-15, and the last push to master was on 2026-09-08. The version string in the working tree is lower than the latest tag, which is unusual and means the branch state and the published artefact are not the same thing. If you install from npm you get 3.2.4, and if you build from the branch you are building something the maintainer has not released.
How it differs from a full feed reader library
The comparison worth making is with a general purpose feed parsing or aggregation library, and the difference is one of scope rather than of capability.
A full feed reader does more work for you. It will fetch, parse, normalise across RSS versions and Atom, deduplicate by guid, sort by date, apply read and unread state, and often persist it all. rss-feed-emitter does the narrow part: it fetches, it parses with feedparser, it remembers what it has seen, and it emits an event.
That narrowness is the feature. Because it does not maintain a store of items, it does not need a database, and the in memory history is a de-duplication set rather than a content store. So it slots into an application that already has somewhere to put the data, and it leaves the decision of what to keep to you.
The second difference is the interface style. The module is an EventEmitter with an add method, and it uses the Node conventions of a per feed event name and a destroy method to tear the instance down. A feed reader library more often exposes a promise based fetch, or a callback, or a stream. The emitter model is the right one for a service that reacts to many feeds, since a single listener can serve every feed and per feed event names give you the routing.
The third difference is the polling model, and that is where the comparison is least favourable. A modern aggregator built on undici or on the native fetch will handle conditional requests properly, which matters for both bandwidth and politeness, and it will be on a maintained HTTP stack. rss-feed-emitter predates that and has not moved, and the v3 rewrite was about ES6 and the module system rather than about the transport.
Editorial conclusion
rss-feed-emitter fits a Node.js service that needs to react to new entries in a set of feeds, since the polling, the deduplication of already seen items and the event emission are handled for you and the interface is four methods. It does not fit a project that needs a real time feed reader with conditional GET support, since the module polls on a timer, and it does not fit a codebase that has moved off the deprecated request library style dependency set. Before adopting it, check that the request and feedparser dependencies satisfy your security review, set a user agent because the default is not yours, and note that the newest tagged release is v3.2.4 from 2025-11-15 while the last push was on 2026-09-08.
Frequently asked questions
How do I install and use rss-feed-emitter?
Install with npm install rss-feed-emitter, then create an instance with new RssFeedEmitter(), add a feed with feeder.add({ url, refresh }) and listen for the new-item event with feeder.on('new-item', ...). The default refresh interval is 60 seconds, and you can attach an error handler with feeder.on('error', console.error) to avoid an unhandled error event.
How do I stop rss-feed-emitter from emitting the existing feed items on the first poll?
Construct the instance with skipFirstLoad set to true. The first poll is then treated as a baseline and its items are recorded without firing events, so only items published after that point are emitted.
Can rss-feed-emitter use different event names for different feeds?
Yes. Each feed config accepts an eventName property, and the emitter then fires that event instead of new-item for that feed. A consumer can listen to new-item for everything and to the named event for one specific source.
What are the runtime dependencies of rss-feed-emitter?
Two: feedparser at 1.1.4 for parsing RSS and Atom, and request at ^2.88.2 for HTTP. The package requires Node.js 8.16.2 or newer, and older Node.js lines were supported by earlier version ranges, 4.x through 7.x until version 2.0.1 and 0.10.x and 0.12.x until version 1.0.7.
Is rss-feed-emitter still maintained?
The last push to master was on 2026-09-08 and the most recent tagged release is v3.2.4 from 2025-11-15, with v3.2.3 from 2022-02-12 before it. The package.json in the working tree still reads version 3.0.0, so the published artefact and the branch are not the same state.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/filipedeschamps-rss-feed-emitter)Was this useful?