Connecting my app to the HackerNews Firebase API

In my last article, I showed how I built a HackerNews feed with React and styled-components. In the meantime I could connect this little news feed to the official HackerNews Firebase API. Like the last time, I will show you how I did it and what I experienced while doing so.

As last time, the full code is available on GitHub.

Preface

I wanted the app to always fetch the newest stories from HackerNews. If you already had a look on the description of the API you may have noticed, that the endpoint provides ID values for the newest 500 stories. Since paging is not in the scope of this article, I manually limited the number of stories to the desired amount.

Choosing a Firebase client library

As the README states, you can use every Firebase client library. So I decided to start with ReactFire. Since I am working with React, it was the logical choice for me.

Soon I found out, that using ReactFire also means, that you have to use the ReactFireMixin to extend the React component’s functionality with Firebase-specific methods. Now that’s where I faced a problem: The preferred way of creating a component is either using a simple function or an ES6 class. Mixins on the other hand, can only be used with React.createClass({...}). Also, Mixins are considered harmful. So either I used Mixins and createClass which is not the preferred way, or I had to find another solution.

That’s when I found out about Tyler McGinnis who had the same issues and had created re-base as a solution. After a little bit of playing around with it, I sticked to re-base since it provided an easy solution for what I wanted to do.

Adding re-base to the app

I wanted the root component to fetch the Stories and pass them down to the StoryList and Story components I created the last time. So I set up re-base like this:

 

Setting up re-base for further use in the App component

I split the base URL and the versioning part. If you would use https://hacker-news.firebase.com/v0 as the database URL, you would get the following error message:

The error you get for including the version child path to the database url

So I ended up putting the re-base object in a separate Api.js file to be able to import it where I need it and build some wrapper functions to dynamically include the versioning part. This way there would be no difference in the usage, and you don’t have to add the version to the endpoint, every time you want to access the database:

 

A wrapper class around the re-base object to include the versioning part to the endpoint URL

Fetching strategy

Before starting, I thought about possible solutions for fetching the data, because of the way the API is structured.

The HackerNews API provides several endpoints. I ended up using two of them:

  • /newstories: Provides a list of up to 500 story IDs, the newest first.
  • /item: On HackerNews, everything is an item. This could be a story, commend, job, even a poll.

That means, that every time we fetch the newest stories from the API, we have to do at least two AJAX calls, to get all the information we need, and then update the state of the component.

  1. Get the story IDs
  2. Fetch the data for each of the stories
  3. Set the state array with all of the fetched story objects

Now, having an idea about the solution, I started extending the Appcomponent.

Fetching the newest stories from HackerNews

To fetch the IDs and the data for the newest stories, I used the fetch function of re-base, which was fairly easy. You just have to pass the endpoint and an options object containing, among others, the callback and the context of your component. You can omit the latter, if you are not calling a function of your component within the callback. If you do, include context: this and call your function with this.myFunction() within the callback.

But let’s have a look at the code:

 

The new App component

The first thing I did here, was calling the fetch function within componentDidMount. Since I call one of the App component’s functions, I had to pass the context object. So after fetching the IDs of all new stories, fetchNewStories is called.

There, as mentioned at the beginning of this article, I manually take the first 30 IDs with storyIds.slice(0,30). Also, I call fetchSingleStory for each ID by using map(this.fetchSingleStory) which returns an Array of Promises into an object called actions. The next step is crucial: I used Promise.all(...) and chained the update of the component’s state to the resulting Promise. This way, fetchNewStories will always wait until the data for all of the stories has been fetched, before updating the app’s state in one single call.

In my first attempt, I always set the state each time a story has been fetched, so basically I called this.setState 30 times in a row, and could be seen every time the list was rendered.

EDIT: As Yan Yankowski mentioned, I should have moved the fetchSingleStory function outside the component instead of making it a public method of the App component. I also think, this would have been the better approach, so I keep this in mind for future components 🙂

Closing words

Again, I could learn a lot while building this little feature. I spent quite some time doing research for finding a proper library to connect the app to the API, but most of the time was invested in solving the problem to chain the two API calls together and wait for them to finish before setting the state. This StackOverflow answer helped me a lot.

 

Leave a Reply

Your email address will not be published.