Node REST APIs with Firebase
Minimal and powerful REST APIs using cloud functions
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
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!
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.