Node REST APIs with Firebase

Minimal and powerful REST APIs using cloud functions

Node REST APIs with Firebase

Motivation

As a chiefly front-end web developer who dabbles in the backend, I hate spending too much time exploring the different databases, hosting services, security packages, and an endless number of ORMs for whichever language you decide to build your API in.

Databases are hard to manage, not to mention having to securely manage connections to and from them.

As for simply deploying an API, your options are surprisingly sparse. AWS EC2 instances are way too expensive (plus steep learning curve to configure), Heroku is fine besides it's very annoying "sleep" time for dymos, and Qovery which is really early in its life and has a lot of bugs.

Enter Firebase. Firebase allows you to deploy always-on REST APIs in just minutes, afforably. Plus, you can quickly and easily connect a Firestore NoSQL database to your API. This cuts out almost 90% of the backend setup work and allows you to proceed with building practical software that solves real world problems.

firebase-1024x293.png

Firebase

Start by installing it

npm install -g firebase-tools

First, we need to login

firebase login

Once installed and logged in, initialize the product. In this case we are just going to start with Functions and we can easily hook up our Firestore when the time comes.

firebase init

Arrow down to Functions, hit the space bar at "Functions: ...", and hit enter. While still initializing, create a new firebase project (or use an existing one). Continue following the prompts and you should end with this:

✔  Firebase initialization complete!

Great! Next let's build our project. Notice I didn't "install dependencies with npm" - I personally prefer yarn to manage my node packages. It's a preference thing, the actual differences are minimal to non-existent so if you installed with npm, you are all good.

If you didn't install with npm, do so now with yarn

cd functions/ && yarn

Lets add some packages to make this an Express REST API for real.

yarn add express cors body-parser

Building the Server

With all the setup complete, let's build a simple express api. If you are familiar with building express applications (or really any application with any language), this will look familiar. functions/index.js

const functions = require('firebase-functions')
const app = require('express')()
const cors = require('cors')
const {json} = require('body-parser')

app.use(cors())
app.use(json())

// @GET /status
app.get('/status', (req, res) => {
  res.send({status: 'healthy'})
})

exports.api = functions.https.onRequest(app)

That last line is the most important: that tells firebase that we are exporting (as api) HTTPS request functions, i.e. a REST API.

Deploy

To deploy, simply run the following (go back to the root level, so don't be in the functions/ folder when you run this)

cd .. && firebase deploy

And there you have it! If you want to run it locally for testing run the following

firebase serve

If you ran firebase serve, the command line will spit out a localhost endpoint that you can test on. To see your live api endpoint, visit the firebase console once again, click on Functions and the first entry (should be titled api) will have the full url in gray letters right below it. Give it a go!

Screen Shot 2021-08-31 at 2.16.15 PM.png

Additional: Connecting to your Firestore

The last and coolest piece of all this is actually reading and writing to a database with your newly built api. How do we connect the project firestore?

Start by visiting the firebase console. Click on your project and navigate to the Firestore. Get it started in test mode and leave it be. Thankfully with the functions and the store in the same project, you don't have to manage connections! For now at least.

Go back to your functions and, in the same functions/index.js file, connect to your database!

const admin = require('firebase-admin')
admin.initializeApp()
const db = admin.firestore()

Next, add the following new route.

// @POST /monitor
app.post('/monitor', async (req, res) => {
  const {start, end, records, device} = req.body

  db.collection('monitor')
    .doc(`${start}|${end}`)
    .set({start, end, records, device})
    .then(doc => console.log(doc))
    .catch(err => console.log(err))

  res.send({success: true})
})

Notice that you can connect to the firestore via the new db constant that you just created! It works much like MongoDB in how you access and write to the db, so read up on the documentation for more.

Your final functions/index.js file should look like the following

const functions = require('firebase-functions')
const app = require('express')()
const cors = require('cors')
const {json} = require('body-parser')

// Connect to the firestore db
const admin = require('firebase-admin')
admin.initializeApp()
const db = admin.firestore()

app.use(cors())
app.use(json())

// @GET /status
app.get('/status', (req, res) => {
  res.send({status: 'healthy'})
})

// @POST /monitor
app.post('/monitor', async (req, res) => {
  const {start, end, records, device} = req.body

  db.collection('monitor')
    .doc(`${start}|${end}`)
    .set({start, end, records, device})
    .then(doc => console.log(doc))
    .catch(err => console.log(err))

  res.send({success: true})
})

exports.api = functions.https.onRequest(app)

And there you have it! Cheers.