Making an ASync Call With Youtube-Node and Redux

Asynchronous calls can always cause issues for any Javascript developer.  Working with React and Redux is no different.  The easiest way to work with Asynchronous calls with Redux is to use the Redux Promise package.  Its a simple middleware that will determine whether or not the action contains a promise.  If it is determined that the action payload contains a promise, it will resolve it before sending it over to a reducer and return the results of the call.  However, I recently ran into an issue with an async call that that the Redux Promise package did not resolve.

Summary of the Problem

The issue I ran into recently was with the Youtube-Node NPM package, while working on a personal project of mine.  Redux Promise did not detect calls to YouTube from this package as a promise, so the call to the YouTube api would not properly resolve and therefore no results were returned.  The issue of course, was that the call to YouTube was still taking place while the rest of the application finished loading.

Redux Docs

According to the Redux documentation, when working with async you should have three actions for async calls:

  1. An action informing the reducers that a request began.
  2. An action informing the reducers that the request finished successfully.
  3. An action informing the reducers that there was an error.

Full disclosure (I did not yet create an action for handling errors).

How My Solution Works

When a user performs a search, the following Redux action is triggered:

  payload: searchTerm

The payload (searchTerm), is whatever the user searched for. Instead of going right to the reducer, there is a custom middleware that will determine if the action type is FETCH_VIDEOS. If so, then that lets our application know that this is an asynchronous call. The middleware will then make an api call. Once its all resolved, a new action called RECEIVE_VIDEOS will be triggered from the middleware. The payload of this action is the videos from YouTube. This action would look something like this:

  payload: videos

Now that we’ve got the videos, we can send this action right to our reducer to update the application state.

Below is what the middleware looks like.

const ytMiddleware = store => next => action => {
    if (action.type == FETCH_VIDEOS) {
        let youTube = new YouTube();
        youTube.setKey('YOUTUBE KEY');, 10, {pageToken: ''}, function(error, result){
            if(error) {
                return 'error';
            } else {
                let videos = result.items;
                let videoList = [];
                for(let i=0; i<videos.length; i++) {
                    let video = {};
                    video.thumbnail = videos[i].snippet.thumbnails.medium.url;
                    video.title = videos[i].snippet.title;
    return next(action);

Application State

Earlier in this post, I referenced the Redux docs. As mentioned above, they recommend having three actions. The first action is to let your state know that an async call is being made. This is what FETCH_POSTS will do. Therefore, the application state has a property called videos, which has two properties itself:

  1. isFetching
  2. videoList

The state object then hypothetically looks something like this:

  videos: {
    isFetching: true,
    videoList: [video1, video2, video3]

The FETCH_VIDEOS action not only triggers the api call to YouTube in the middleware, but in the reducer, it will set the state of isFetching to true. By doing this, your state is aware of the api call taking place and you can easily present the user with a spinner or some sort of widget to indicate that something is loading. Once the RECEIVE_VIDEOS action hits the reducer, it will set the isFetching property to false. This fulfills the second action that the request finished successfully. While I didn’t set up an error workflow (third action above) just yet, you could easily use this same logic to present the user with an error if there were any issues receiving the videos.


Overall I’m happy that this solution worked. While it is possible that there are other third party libraries and packages that could have easily solved this problem, I’m not yet aware of them (I’m still new to the React scene). I hope that this solution might be able to help somebody in the future.