Create an Observable-Based Fetch Wrapper Using Ramda & Most

Josh Burgess
InstructorJosh Burgess
Share this video with your friends

Social Share Links

Send Tweet
Published 7 years ago
Updated 5 years ago

Unlike RxJS, Most does not include an observable-based AJAX helper utility. However, it can be very convenient to have one, because it allows you to use streams to do things like declaratively handling errors & setting up cancellation. In this lesson, we use currying & functional composition to create an Observable-based wrapper around the fetch API with Ramda & Most.

[00:00] First, we need to import compose, curry, and head from Ramda. Then we need to import from promise and observe from Most. Because we're using Node for this lesson, we need to import fetch from Node fetch. Below that, we have an end point for the GitHub API which searches for repos containing the word most.

[00:23] Let's start by writing a functional wrapper for the promise.then method. This will be a curry to function to give us more flexibility, and it will take in two arguments, a function F and [inaudible] which in this case will be a promise. It just returns the result of calling the native.then method on the [inaudible] and passing it function F.

[00:45] Next, let's define a function called two JSON which just takes in the fetch response and returns the result of calling the JSON method on that response. We'll use both of these functions to help define our observable base fetch wrapper. Let's call this wrapper function fetch JSON stream since it will always return a stream of JSON results.

[01:08] We'll use Ramda's compose function to create a functional pipeline which starts by calling fetch and then passes the response to a new function created by calling then with two JSON as its first argument and then passes the resulting JSON to Most from promise function which just converts promises into observables.

[01:28] Now, we're going to use fetch JSON stream to call the GitHub API and make sure everything's working correctly. This API call will probably return a lot of results so let's write a couple of helper functions to log out only the first result.

[01:41] First let's define two items which takes in an object and uses destructuring to return the value for the item's key. Then we'll use functional composition again, this time to compose two items with Ramda's head function which just returns the first item in an array. We'll call this function two first item.

[02:01] We can define a log function called log first item by composing together console.log and two first item. Now, let's use Most observe function. Log first item will be triggered when the JSON results are emitted through the resulting stream of applying fetch JSON stream to the GitHub API URL.

[02:22] Now, let's run the file to see what happens. As expected, it looks like a JSON object has been logged out. If we scroll up, we can see that the first item returned by this query is the official Most JS repo.

[02:42] Now, we have a reusable fetch function which always returns a stream which is nice because it allows us to do things let declaratively handling errors using Most recover with function and setting up automatic cancellation using functions like until and take while.

Vinny
Vinny
~ 7 years ago

Is there a particular advantage to using Most.JS as a reactive lib as opposed to using RxJS ?

Josh Burgess
Josh Burgessinstructor
~ 7 years ago

Is there a particular advantage to using Most.JS as a reactive lib as opposed to using RxJS ?

Good question. First, you can read the authors' own words about why you might choose Most here:

Why most.js for Reactive Programming?

Now, I'll give you my opinion, in my own words:

There are 3 major reasons to use Most over RxJS:

  1. Most offers a functional API in addition to a method chaining/fluent API, whereas RxJS only offers a method chaining/fluent API.

The concept is similar to how Ramda & Lodash/fp differ from the regular version of Lodash or Underscore. In Ramda & Lodash/fp, pretty much all functions that take in more than one argument are auto-curried, and the data collection (an array or object) is passed in last. This allows you to create reusable functions by simply only passing in certain arguments and delaying passing in the rest.

Most's functional API works in this same way, but the data collection argument is a stream rather than an array or object. This is important, because it enables you to use a pointfree style where you can use functional composition (ex: via functions like Ramda's compose or pipe) to combine multiple functions together into new functions without having to mention the arguments, which a lot of people find to be more clear & concise. You can see examples of this style here in this lesson on lines 9, 12, & 13. Notice that I created new functionality on those lines without having to write any information about the arguments they take in or how they get passed from one intermediary function to the next.

However, an important thing to note is that the functional API in the current version of Most is not auto-curried. This means that you need to use an auto-curry utility function like curry from Ramda or Lodash/fp on the Most functions if you want to be able to partially apply arguments to them, but this will no longer be the case in the future. Most 2.0 will feature an auto-curried functional API, like Ramda & Lodash/fp (but for streams), where all functions are already curried for you. Here's an example taken from the docs from my redux-most library which explains this:

// Functional & Pointfree style using currying & functional composition
import { compose, curry, pipe } from 'ramda'
import { debounce, filter, map } from 'most'

// NOTE: Most 2.0 will feature auto-curried functions, but right now we must curry them manually.
const curriedDebounce = curry(debounce)
const curriedFilter = curry(filter)
const curriedMap = curry(map)

// someEpic is a new function which is still awaiting one argument, the action$
const someEpic = compose(
  curriedMap(someFunction),
  curriedDebounce(800),
  select(SOME_ACTION_TYPE)
)

// someOtherEpic is a new function which is still awaiting one argument, the action$
// pipe is the same as compose, but read from left-to-right rather than right-to-left.
const someOtherEpic = pipe(
  selectArray([SOME_ACTION_TYPE, SOME_OTHER_ACTION_TYPE]),
  curriedFilter(somePredicate),
  curriedMap(someFunction)
)
  1. Performance.

Most.js is very, very fast. The authors of Most take performance seriously, and this has resulted in Most being, generally, faster, and, at times, SIGNIFICANTLY faster than RxJS 5 & other reactive programming libraries. Please, see the benchmarks here.

  1. Fantasy Land spec compliance.

Another difference between Most and RxJS is that Most implements the Fantasy Land spec. You can learn more about Fantasy Land here. It basically provides a common spec for libraries to define ADTs (Algebraic Data Types) in a way that lets them interoperate with each other seamlessly. You don't HAVE to use this feature at all, but it's nice if you're already using ADTs in other libraries as it opens up a lot of new FP possibilities.

So, it has both a more flexible API and all around better performance. Other than that, it's also a bit more stripped down & minimal (in a good way), and it tends to favor being declarative just a little bit more than RxJS. Most tries to guide you into consistently writing clean, declarative code. Imperative methods are actively discouraged and/or only offered as 3rd party packages outside of the main repo. For example, Subjects are available, but they're in a separate package, because they're an imperative tool and often not needed. There's also a community organization which features other useful & cool, experimental additions.

Hope that helps! :)

Vinny
Vinny
~ 7 years ago

I truly appreciate your response Josh. After going through Luis Atencio's book Functional Programming in JavaScript - which was a great intro to FP, I still could not figure out how to compose RxJS streams in a pointfree manner. While Rx is declarative in its implementation, I still feel like it doesn't fully adhere to FP paradigms. It's awesome to know that MostJS provides a flexible API that enables one to take advantage of function composition. Bonus points for the performance! I'm really excited to use Most in my future projects. Thanks for the help. I'm looking forward to more of your videos.

Markdown supported.
Become a member to join the discussionEnroll Today