Using Firebase for storage and auth with Restify & NodeJS

Colin Wren
May 8 · 8 min read
Photo by Adam Wilson on Unsplash

I’ve recently been building an API that implements basic Create, Read, Update, Delete (CRUD) functionality for a CV application I’m going be building.

I built the API consumer first using Pact and once I was happy that my API met the consumer contract with stubs I moved to implement Firebase for storage and auth, using the consumer contract to ensure I didn’t break anything.

It was my first experience using Firebase as I normally fallback to my comfort zone of Django and PostgreSQL if I need to implement authorisation but I’d used Restify in the past and enjoyed it so decided to give Firebase a shot, as it promised easy to implement auth and storage.

Adding Authentication with Firebase

Photo by Siora Photography on Unsplash

Out of the box Firebase supports a pretty impressive number of authentication backends and I think you’d struggle to find a user who doesn’t have at least one account with the backends offered, at time of writing these include:

  • Email address and password
  • Email address and one-time link
  • Phone number
  • Facebook
  • Twitter
  • Google
  • Microsoft
  • Apple

In the following examples I’m using email address and password.

Before you can log a user in, you need to sign them up for your application.

In order to do this you can use the firebase.auth().createUserWithEmailAndPassword() function which creates the account or throws an error and then use the firebase.auth().currentUser.getIdToken() function to get an auth token for that user.

Wrap a try/catch around this and you’ve got a means of signing the user up and returning their auth token to them

Once the user has logged into your application for the first time they can then be logged in using the firebase.auth().signInWithEmailAndPassword() function, which, similar to the createUserWithEmailAndPassword() function logs them in but requires the call to getIdToken() to get their auth token.

Wrap a try/catch around this and you’ve got a means of singing the user in and returning their auth token to them

Once the user has signed up or logged in successfully and they’ve got their auth token they’ll then need to use the token to authenticate themselves in subsequent calls to the API, this is often done by using it as a Bearer token in the Authorization header (e.g. Authorization: Bearer [TOKEN] ).

In Restify you can use functions as middleware to be applied to all calls to the server and perform tasks before the controller is called.

I created a function to do this and then applied it using the server.use() function, the middleware functions take the same (req, res, next) arguments as normal controllers.

Grab the JWT, get the user from it and set that on the request to be accessed by later controllers

In order to get the user’s record from the JWT you need to call admin.auth().verifyIdToken() which returns the information contained in the token. You can then use the sub property with admin.auth().getUser() to load the user record.

Once I got the user record I used Restify’s req.set() function to set the user object on the request, this can then be used via req.get() in subsequent controllers to access the user record.

The Firebase documentation suggests that to end the user’s session you call firebase.auth().signOut() but this does not actually revoke the user’s authorisation token, meaning that the user can still access documents for up to an hour after the token they’re using was created.

Instead signOut() tells Firebase to not renew the user’s token should it expire. I’ve not found a means of fully revoking access on logout yet, so this is something to bear in mind when using Firebase for auth.

Using Firebase as storage

Photo by Danny on Unsplash

Firebase uses a document store structure. Similar to most NoSQL solutions it’s not bound to a schema and as such can be used to hold varying data between documents.

I’ve not had the opportunity to dive too deeply into the storage side of Firebase yet but from what I’ve seen so far it’s relatively easy to use, but like most NoSQL storage you need to plan ahead for how you’ll scale it out.

Firebase uses collections as a means of separating the document types so if you’re looking to work with a series of documents holding data on dogs for instance you would use admin.firestore().collection('dogs') and then perform the operations against the collection.

Inside the collection there are a series of references to documents; these reference objects are how you interact with creating, deleting, updating and reading the document.

In order to work with Firebase storage you need to initialise the Firestore using admin.firestore() , which returns an object you can use to perform the document operations with.

The db constant will then be used to perform any database operations.

Creating a document is really simple, you just create a new document in the collection using db.collection('dogs').doc() which will return a reference to the newly created document.

That document reference has a method called set() that is used to update the data stored in that document.

The document reference is really important for dealing with the record

Once you’ve created a document it’s likely you’ll want to retrieve it later to interact with it.

While it’s easy enough to do this if you’ve still got the ref stored somewhere it’s often the case that you’ll need to do a search to find it or do a search to a number of documents based on a condition (such as the user ID to get all documents for that user).

In order to search for a document you need to get a reference for the collection the document is stored in, this reference has a method called where() which can be used to provide a predicate to search with.

Once the search is performed by using the get() function provided by where() the resulting object will have a docs property which is an array of the documents returned.

In the following example I do a search for all the documents associated with the user and then return an array of IDs to the user.

The predicate can search in nested objects using dot syntax

One thing to note is that as Firebase is a NoSQL database. You’ll need to find a means in your schema to store properties that you’ll want to search on such as userId as these won’t be provided by default, in my app I’ve ended up having to extend the schema of my document’s meta object to include the userId in order to search on it.

Directly accessing a document via the document ID in order to read the data from it is managed in a similar manner to performing a search, the only difference being that providing the collection with a document ID means there’s no need to provide a predicate.

You can get the reference to the document using db.collection('dogs').doc(DOCUMENT_ID) which returns the reference to the document in the collection, similar to the search you then need to call get() to get the reference to the document itself.

Once you’ve got the document reference you can use the reference’s exists property to check the document actually exists and call the data() method to read the document data itself.

I like to split the collection reference logic into a separate function to allow for re-use

Updates to a document are performed on the document ref and are provided as a single depth object.

Any nested object properties that need to be updated need to be provided in dot syntax (e.g. to update the userId property of the meta object you need to provide meta.userId as a key in the update object).

When dealing with the need to use dot syntax for nested objects I found the dotize library useful.

Once you’ve got the update data in the correct format you then call the update() method on the document reference.

The update to the document however does not update the existing document reference so you’ll need to fetch it again and read the data from it if you’re looking to return the updated document to the user.

Dotize is great for taking nested objects and converting them to dot syntax

Deletion of a document is really straight-forward, it’s just simply a case of calling delete() on the document reference.

I return true or false as a means of knowing if the document existed but this isn’t necessary

Stubbing Firebase for testing

Photo by Nicolas Thomas on Unsplash

As mentioned at the start of this post, I’ve been using Pact to practise TDD while building my API implementation, and because of the static nature of the pacts created by Pact I need to use stubbed values in my tests.

Even if I wasn’t using Pact however I would still be looking to stub my Firebase calls as this would allow me to test how my application will respond to various scenarios without the need to have a running Firebase to connect to and to manipulate into returning the states I’d need to verify.

I’m using Jest as my test runner because it’s got an incredibly useful auto-mock feature that means that putting modules in a __mock__ directory at the root of the project will apply those modules as stubs for the actual modules imported in the code under test.

This replaces the need to use libraries such as proxyquire and Rewire in order to control the libraries the code under test will interact with.

firebase-admin is the module that provides the JWT reading and firestore functionality so it’s where the majority of the firebase calls are made.

The following module needs to be available at __mocks__/firebase-admin.js in order to work, you can use mockImplementation on any of the jest.fn() calls to implement behaviour you want to test.

This mock covers all the firebase-admin functionality used in this post

firebase/app is the module that provides the functionality to create new user accounts and log them in and out, as well as get the current user. T

hat being the case it’s a relatively lightweight mock.

The following module needs to be available at __mocks__/firebase/app.js in order to work, similar to above you can use mockImplementation to implement behaviour you want to test.

This mock covers all the firebase/app functionality used in this post

Summary

Firebase definitely delivers on it’s promise of easy to implement auth and storage and it’s a pleasure to work with, especially when combined with a modular approach using middleware in Restify and Jest’s auto-mock functionality during test.

I’ve yet to get to a point where I need to worry about Firebase’s pricing model so I haven’t taken that into account but I would recommend Firebase for any developer looking to add auth and/or storage to a project quickly.

Did you know that we have four publications? Show some love by giving them a follow: JavaScript in Plain English, AI in Plain English, UX in Plain English, Python in Plain English — thank you and keep learning! We’ve also launched a YouTube and would love for you to support us by subscribing to our Plain English channel

And as always, Plain English wants to help promote good content. If you have an article that you would like to submit to any of our publications, send an email to submissions@plainenglish.io with your Medium username and what you are interested in writing about and we will get back to you!

JavaScript In Plain English

New articles every day.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store