Intro to Hoodie and React

Avatar of Jake Peyser
Jake Peyser on

Let’s take a look at Hoodie, the “Back-End as a Service” (BaaS) built specifically for front-end developers. I want to explain why I feel like it is a well-designed tool and deserves more exposure among the spectrum of competitors than it gets today. I’ve put together a demo that demonstrates some of the key features of the service, but I feel the need to first set the scene for its use case. Feel free to jump over to the demo repo if you want to get the code. Otherwise, join me for a brief overview.

Setting the Scene

It is no secret that JavaScript is eating the world these days and with its explosion in popularity, an ever-expanding ecosystem of tooling has arisen. The ease of developing a web app has skyrocketed in recent years thanks to these tools. Developer tools Prettier and ESLint give us freedom to write how we like and still output clean code. Frameworks like React and Vue provide indispensable models for creating interactive experiences. Build tools like Webpack and Babel allow us to use the latest and greatest language features and patterns without sacrificing speed and efficiency.

Much of the focus in JavaScript these days seems to be on front-end tools, but it does not mean there is no love to be found on the back-end. This same pattern of automation and abstraction is available on the server side, too, primarily in the form of what we call “Backend as a Service” (BaaS). This model provides a way for front end developers to link their web or mobile apps to backend services without the need to write server code.

Many of these services have been around for awhile, but no real winner has come forth. Parse, an early player in the space, was gobbled up by Facebook in 2013 and subsequently shut down. Firebase was acquired by Google and is slowly making headway in developing market share. Then only a few weeks ago, MongoDB announced their own BaaS, Stitch, with hopes of capitalizing on the market penetration of their DB.

BaaS Advantages

There are an overwhelming number of BaaS options, however, they all have the same primary advantages at their core.

  • Streamlined development: The obvious advantage of having no custom server is that it removes the need to develop one! This means your development team will perform less context switching and ultimately have more time to focus on core logic. No server language knowledge required!
  • No boilerplate servers: Many servers end up existing for the sole purpose of connecting a client with relevant data. This often results in massive amounts of web framework and DAL boilerplate code. The BaaS model removes the need for this repetitive code.

These are just the main advantages of BaaS. Hoodie provides these and many more unique capabilities that we will walk through in the next section.

Try on your Hoodie

To demonstrate some of the out-of-the-box functionality provided by Hoodie, I am going to walk you through a few pieces of a simple Markdown note taking web application. It is going to handle user authentication, full CRUD of users’ notes, and the ability to keep working even when a connection to the internet is lost.

You can follow along with the code by cloning the hoodie-notes GitHub repository to your local machine and running it using the directions in the README.

This walkthrough is meant to focus on the implementation of the hoodie-client and thus, assumes prior knowledge of React, Redux, and ES6. Knowledge of these, although helpful, is not necessary to understand the scope of what we will discuss here.

The Basics

There are really only three things you have to do to get started with Hoodie.

  1. Place your static files in a folder called /public at the root of your project. We place our index.html and all transpiled JS and image files here so they can be exposed to clients.
  2. Initialize the Hoodie client in your front end code:

    const hoodie = new Hoodie({
      url: window.location.origin,
      PouchDB: require('pouchdb-browser')
    })
  3. Start your hoodie server by running hoodie in the terminal

Of course, there is more to creating the app, but that is all you really need to get started!

User Auth

Hoodie makes user and session management incredibly simple. The Account API can be used to create users, manage their login sessions, and update their accounts. All code handling these API calls is stored in the user reducer.

When our app starts up, we see a login screen with the option to create a user or log in.

When either of these buttons are pressed, the corresponding Redux thunk is dispatched to handle the authentication. We use the signUp and signIn functions to handle these events. To create a new account, we make the following call:

hoodie.account.signUp({ username: 'guest', password: '1234' })
  .then(account => {
    // successful creation
  }).catch(err => {
    // account creation failure
  })

Once we have an account in the system, we can login in the future with:

hoodie.account.signIn({ username: 'guest', password: '1234' })
  .then(account => {
    // successful login
  }).catch(err => {
    // login failure
  })

We now have user authentication, authorization, and session management without writing a single line of server code. To add a cherry on top, Hoodie manages sessions in local storage, meaning that you can refresh the page without the need to log back in. To leverage this, we can execute the following logic the initial rendering of our app:

hoodie.account.get()
  .then({ session, username }=> {
    if (session)
      console.log(`${username} is already logged in!`)
  }).catch(err => {
    // session check failure
  })

And to logout we only need to call hoodie.account.signOut(). Cool!

CRUD Notes

Perhaps the nicest thing about user management in Hoodie is that all documents created while logged in are only accessible by that authenticated user. Authorization is entirely abstracted from us, allowing us to focus on the simple logic of creating, retrieving, updating, and deleting documents using the Store API. All code handling these API calls is stored in the notes reducer.

Let’s start off with creating a new note:

hoodie.store.add({ title: '', text: '' })
  .then(note => console.log(note))
  .catch(err => console.error(err))

We can pass any object we would like to the add function, but here we create an empty note with a title and text field. In return, we are given a new object in the Hoodie datastore with its corresponding unique ID and the properties we gave it.

When we want to update that document, it is as simple as passing that same note back in with the updated (or even new) properties:

hoodie.store.update(note)
  .then(note => console.log(note))
  .catch(err => console.error(err))

Hoodie handles all the diffing and associated logic that it takes to update the store. All we need to do is pass in the note to the update function. Then, when the user elects to delete that note, we pass its ID to the remove function:

hoodie.store.remove(note._id)
  .then(()=> console.log(`Removed note ${note._id}`))
  .catch(err => console.error(err))

The last thing we need to do is retrieve our notes when the user logs back in. Since we are only storing notes in the datastore, we can go ahead and retrieve all of the user’s documents with the findAll function:

hoodie.store.findAll()
  .then(notes => console.log(notes))
  .catch(err => console.error(err))

If we wanted, we could use the find function to look up individual documents as well.

Putting all of these calls together, we’ve essentially replaced a /notes REST API endpoint that otherwise would have required a fair amount of boilerplate request handling and DAL code. You might say this is lazy, but I’d say we are working smart!

Monitoring the connection status

Hoodie was built with an offline-first mentality, meaning that it assumes that clients will be offline for extended periods of time during their session. This attitude prioritizes the handling of these events such that it does not produce errors, but instead allows users to keep working as usual without fear of data loss. This functionality is enabled under the hood by PouchDB and a clever syncing strategy, however, the developer using the hoodie-client does not need to be privy to this as it is all handled behind the scenes.

We’ll see how this improves our user experience in a bit, but first let’s see how we can monitor this connection using the Connection Status API. When the app first renders, we can establish listeners for our connection status on the root component like so:

componentDidMount() {
  hoodie.connectionStatus.startChecking({interval: 3000})
  hoodie.connectionStatus.on('disconnect', () => this.props.updateStatus(false))
  hoodie.connectionStatus.on('reconnect', () => this.props.updateStatus(true))
}

In this case, we tell Hoodie to periodically check our connection status and then attach two listeners to handle changes in connections. When either of these events fire, we update the corresponding value in our Redux store and adjust the connection indicator in the UI accordingly. This is all the code we need to alert the user that they have lost a connection to our server.

To test this, open up the app in a browser. You’ll see the connection indicator in the top left of the app. If you stop the server while the page is still open, you will see the status change to “Disconnected” on the next interval.

While you are disconnected, you can continue to add, edit, and remove notes as you would otherwise. Changes are stored locally and Hoodie keeps track of the changes that are made while you are offline.

Once you’re ready, turn the server back on and the indicator will once again change back to “Connected” status. Hoodie then syncs with the server in the background and the user is none the wiser about the lapse of connectivity (outside of our indicator, of course).

If you don’t believe it’s that easy, go ahead and refresh your page. You’ll see that the data you created while offline is all there, as if you never lost the connection. Pretty incredible stuff considering we did nothing to make it happen!

Why I Like Hoodie

Hoodie is not the only BaaS offering by any means, but I consider it a great option for several reasons

  1. Simple API: In this walkthrough, we were able to cover 3 out of 4 of the Hoodie APIs. They are incredibly simple, without much superfluous functionality. I am a big fan of simplicity over complexity until the latter cannot be avoided and Hoodie definitely fits that bill.
  2. Free and self-hosted: Putting Hoodie into production yourself can seem like a drag, but I believe such a service gives you long-term assurance. Paid, hosted services require a bet on that service’s reliability and longevity (see: Parse). This, along with vendor lock-in, keep me on the side of self-hosting when it makes sense.
  3. Open Source: No explanation needed here…support the OSS community!
  4. Offline-first: Hoodie provides a seamless solution to the relevant problem of intermittent connectivity and removes the burden of implementation from developers.
  5. Plugins: Hoodie supports 3rd party plugins to provide support for additional server-side functionality outside the scope of the API. It allows for some clever solutions when you begin to miss the flexibility of having your own server.
  6. Philosophy: The developers who built and support Hoodie have clearly thought hard about what the service represents and why they built it. Their promotion of openness, empowerment, and decentralization (among other things) is great to see at the core of an open source project. I love everything about this!

Considerations

Before you make the call to cut ties with your server in favor of a BaaS like Hoodie, there are some things you should consider.

Do you favor increased development speed or future flexibility? If the former is your priority, then go with a BaaS! If you really care about performance and scale, you’re probably better off spinning up your own server(s). This points toward using a BaaS for an MVP or light-weight app and creating a custom server for well-defined, complex applications.

Does your app require integration with any 3rd party services? If so, it is likely you will need the flexibility of your own server for implementing your own custom implementation logic rather than constrain yourself to a Hoodie plugin.

Lastly, the documentation for Hoodie is severely lacking. It will help you get started, but many API definitions are missing from the docs and you will have to fill in some of the blanks yourself. This is mitigated by the fact that the interface is extremely well thought out. Nonetheless, it makes for a frustrating experience if you are used to complete documentation.

Conclusion

For front end developers, using a BaaS is a great prospect when considering your options for creating a web application. It avoids the need for writing server logic and implementing what essentially amounts to a boilerplate REST API. Hoodie delivers this possibility, with the added bonus of a clean interface, simple user management, and offline-first capabilities.

If all you need is a simple CRUD application, consider using Hoodie for your next app!

Additional Resources