Making a Serverless React App using Firebase

Making a Serverless React App using Firebase

Originally posted on the Cratebind Blog

Last week I made the questionable decision to refactor a Node-Express app into a React app.

It forced me to reflect and ask myself some big questions: How am I going to store data? Do I need to build my own API just to make database queries? How will I keep my state synced? Why am I doing this to myself?

The solution, courtesy of Wes Bos, came in the form of Firebase and Re-base. Since this was my first real React app, I had no idea you could use something like Re-base’s syncState to keep your frontend and backend, well, in sync.

You don't need a deep understanding of Web Sockets and you don't need to set up a React server alongside a frontend client. It felt like I’d found something unheard of in web development: It just worked.

Getting Started The first thing you’ll need to do is run npm install re-base in your project's directory, or you can use Yarn if you’re so inclined. You can also check out the GitHub repo for the (almost) finished project.

While that’s running, go ahead and make a Firebase account and project for your app, then go to the “database” tab and hit “get started”.

For testing purposes and only for testing purposes, go to the “Rules” tab and change “.read” and “.write” to “true”.

Google will get mad at you for doing this, and rightly so, but if you’re testing locally this is fine. But do not do this on production until you set up user authentication.

Add Firebase To Your Web App Go back to your project’s main page and click “Add Firebase To Your Web App” and grab the config object that comes up. Specifically, we’re going to need the apiKey, authDomain and databaseURL.

Then in your project root, you’re going to create a firebase config module and add in your credentials:

import Rebase from 're-base';

const base = Rebase.createClass({
  apiKey: '<API KEY>',
  authDomain: '<AUTH DOMAIN>',
  databaseURL: '<DATABASE URL>',
});

export default base; view rawbase.js hosted with ❤ by GitHub We’ll save it as base.js and import it to our App.js or wherever you save the state you want to sync with Firebase. For this example, let’s say you have an app that has users and each user has a list of items for a grocery list. Here’s how you’ll want to set it up:

import React, { Component } from 'react';
import './App.css';
import base from './base'; // import firebase config

// ... list of 50 other components...

class App extends Component {
  state = {
    items: [],
  }

  // this runs right before <App> rendered  
  componentWillMount() {
    // syncState keeps the React state synced to the firebase database
    // we're using 'user-1' as a placeholder
    this.ref = base.syncState(`user-1/items`, {
      // the entire App component is the context of the state we're saving
      context: this,
      // we want the "items" state to be synced with firebase
      state: 'items',
      // we're storing our state as an array, but if your state is made up of objects, leave this line out
      asArray: true 
    });
  }
}

I’ve broken down what each line is doing in the comments, but here’s the bigger picture: SyncState is doing a ton of heavy lifting and is the reason our app can be “server-less”. Here’s a snippet from the Re-Base docs:

syncState: Two way data binding between any property on your component’s state and any endpoint in Firebase. Use the same API you’re used to to update your component’s state (setState), and Firebase will also update.

The prospect of real-time, two-way data binding sounded too good to be true after everything I’d heard about React, Redux/Flux and the nightmarish world of state management.

It boils down to this: Anytime your React state changes, it’ll send an update to your Firebase database, and vice-versa. That means you could have two people adding and removing items simultaneously and, for the most part, their two states should remain the same.

What problems does this solve? If you have a fairly simple data structure for your app, this setup can solve a ton of issues. It means your React will port to React Native extremely easily, because you’re basically using Firebase as your API and keeping your states synced.

It also means that once you’ve set this up (with user authentication), you only have to worry about your React state. You don’t have to worry about querying/saving to your database, waiting for database responses or any of that.

When should I not use Firebase as my React app's database/API? This is an easy question to skip over, but it’s important to understand that this setup is definitely not a one-size-fits-all solution.

First, Firebase has a tendency to rewrite over data fairly aggressively when trying to sync your state. If your data structure isn’t too complicated this usually isn’t a problem, but if you have heavily nested data you might run into some issues due to Firebase's mutable database structure.

Also, if you have large quantities of data that need to be served and changed, you’re probably better off using a dedicated database and an API to keep things from getting out of hand.

Queries are also slower because Firebase’s Realtime Database is stored in JSON, so you don’t have tables, records or indexes. But if you have this much data, it’s unlikely it would make sense to migrate everything to Firebase in the first place.

For a more comprehensive list of growing pains with Firebase, check out this great post from Crisp.