DEV Community

mkadirtan
mkadirtan

Posted on

Introducing feed-rs: Fastest Feed Parser Built With Rust & NAPI-RS

We all know that Rust is faster than Node.js. But, Node.js performs well for most common operations, such as file reading, database queries, or handling HTTP requests.

Do you know where Node.js sucks and a language like Rust shines? CPU-bound tasks, such as parsing or serializing data, expensive mathematical operations such as encryption or decryption, etc.

I needed a feed parser for my project. It had to be compliant with the spec, however, there are various types of feeds, such as:

  • JSON feeds,
  • Atom feeds,
  • RSS with multiple versions ( RSS0, RSS1, RSS2 ) So, I would much rather parse all of them into a common format than deal with each of their intricacies.

I couldn't find a package in the NPM, that took down the long road and handled these feeds altogether in a single format. Instead, I tried to build one on my own. In my experience, dealing with XML data in Node.js has always been somewhat painful. After realizing that the rabbit hole might go a little deeper than I expected, I started to look for alternative solutions.

Then, I remembered that NAPI exists and I can borrow some code from Rust crates into an NPM package!

Long story short, I've found a beautifully written feed parser in cargo, sprinkled some NAPI sauce onto it, and voila: The fastest Feed Parser in NPM was born.

@nooptoday/feed-rs - npm

Fastest RSS parser with the power of Rust. This package includes Node.js bindings from feed_rs package. Latest version: 0.2.1, last published: 9 days ago. Start using @nooptoday/feed-rs in your project by running `npm i @nooptoday/feed-rs`. There are no other projects in the npm registry using @nooptoday/feed-rs.

favicon npmjs.com

If you are curious about how do you glue the code between Rust and Node.js, here is a very short sneak peek:

#[napi]
pub fn parse(feed_string: String, feed_source: Option<String>) -> Result<models::Feed, Error> {
  let result = parser::parse_with_uri(
    feed_string.as_bytes(),
    feed_source.as_ref().map(|source| source.as_str()),
  );

  match result {
    Ok(feed) => Ok(models::Feed::from(feed)),
    Err(err) => Err(Error::from_reason(err.to_string())),
  }
}
Enter fullscreen mode Exit fullscreen mode

I recommend everyone who deals with CPU-bound tasks on Node.js, to use the NAPI bindings, it is awesome! As a quick guide, you can read more about how to use NAPI-RS to build Node.js packages using Rust.

The Benchmarks?

feed-rs          2367 ops/s, ±0.39%   | fastest
fast-xml-parser  1198 ops/s, ±0.26%   | 49.39% slower
rss-parser:      125 ops/s,  ±2.27%   | slowest, 94.72% slower
Enter fullscreen mode Exit fullscreen mode

As you can see, feed-rs is doing double the amount of work than a highly optimized parsing package.

Top comments (0)