◄︎ Gregor's Portfolio Site
01 Jan 2018

Building a node.js api with firebase integration

In this post we will setup a Node Express server, hook it up to a Firebase Database and deploy to Firebase Hosting. All this can be done through the free Spark tier. You can view a live, styled variation of the finished product here .

Basic Config

As a prerequisite, make sure firebase tools are installed on your system: npm i -g firebase-tools

Then, in the directory of your choice, initialize new Firebase hosting: firebase init hosting

Also initialize dynamic cloud functions. This will install a node.js configuration with all the necessary dependencies to play nicely with Firebase: firebase init functions

When complete the previous step installs a functions directory. Navigate into it and install the Express framework:

cd functions && npm i -S express You can now return to the parent directory and open the code in the text editor of your choice. Locate your index.js file. It should already contain a functions variable. We’re going to set this file up to serve our API using Express, and we’ll modify our firebase.json file to include a rewrite to our sample hello route. If this isn’t your first rodeo, feel free to instantiateExpress any way you choose and create a test route to make sure your code is working. Otherwise you can use the following template:

{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites" : [{
      "source" : "**",
      "function" : "app"
    }]
  }
}
const functions = require('firebase-functions');

const express = require('express'); // load express from node_modules

const app = express(); // instantiate express to use its functionality

const PORT = process.env.PORT || 3000; // set a port. Look to environment variable if available

// ROUTES
app.get('/hello', (request, response) => {
    console.log("a request came in")
    response.json({message: "Welcome to my API"})
});

// Use with firebase serve command instead of app.listen
// make sure route rewrites are listed in firebase.json
exports.app = functions.https.onRequest(app);

Now lets start our server with the following console command: firebase serve --only functions,hosting

This will let us test our first route using Postman or through the browser by sending a GET request to localhost:3000/hello . If it’s working properly we should get a response saying “Welcome to my API”.

With that out of the way, lets deploy the project to Firebase with this console command: firebase deploy

You should now be able to access your API endpoint at the deployed URL printed in your command line. 🎉

Reading from our Database

We now have a working API, so lets take the next step and hook it up to our database. Of course this is a good moment to ensure we have values in our database. Log into https://console.firebase.google.com and put in at least one sample value. Below are the current contents of my demo house database. Feel free to import it into your own database:

[ {
  "name" : "bill's house",
  "color" : "white",
  "address" : "224 NE 111st",
  "rooms" : 4,
  "garage" : null
}, {
  "name" : "steve's house",
  "color" : "yellow",
  "address" : "5101 NE 111st",
  "rooms" : 2,
  "garage" : true
}, {

  "name" : "Janet's House",    
  "color" : "blue",
  "address" : "4201 N Bark Drive",
  "rooms" : 5,
  "garage" : null
} ]

Meanwhile, back in our index.js, firebase-tools gives us access to all we’ll need to integrate the database into our code. At the top of our code, underneath our functions variable, require firebase-admin:

const firebase = require('firebase-admin');
And then create an instance of our Firebase app:
const firebaseApp = firebase.initializeApp(
functions.config().firebase
);

We’ll create a new route to return all houses in our database object as JSON.

app.get('/all', (request, response) => {
console.log("showing all values")
getHouses().then(house=> {
response.json({houses :house})
});
});

Now when a GET request comes in to our ‘/all’ path it’ll receive a response of all houses in our database. At this point your index.js should look something like this . If our goal is to create an API capable of reading data from our database and returning JSON response then we’re done! However, there are a few ways to make this fancier…

Serving up HTML pages

Instead of only returning JSON, we’re going to serve up an entire HTML page. To do this we’re going to make some changes to our public folder. Firebase init created an index.html file for us inside public. Unfortunately we won’t be using it, so go ahead and delete it. In it’s place we will be installing an HTML templating engine.Express works with many templating engines including Pug , Mustache and ejs , but we’ll be using Handlebars.js with the aid of a helper library called consolidate. You know the drill, lets start by installing the dependencies:

npm i -S handlebars consolidate
In our index.js file we’ll require consolidate and install the following methods to our app.
const engines = require('consolidate');
app.engine('hbs', engines.handlebars);
app.set('views', './views');
app.set('view engine', 'hbs');

We’ll complement this by actually creating a directory inside functions called views, and in it create index.hbs . Here’s the content of index.hbs:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>All Houses</title>
</head>
<body>
    <h1>List of Houses</h1>
    <ul>
        
        <li><b></b> <br> <br>color: <br>rooms: </li>
        
    </ul>
</body>
</html>

Your new index.js should look something like this:

const functions = require('firebase-functions'); //for firebase storage
const firebase = require('firebase-admin'); //for firebase database
const express = require('express'); // load express from node_modules
const app = express(); // instantiate express to use its functionality
const PORT = process.env.PORT || 3000; // set a port. Look to environment variable if avaialble
app.engine('hbs', engines.handlebars); // use consolidate to attach handlebars templating
app.set('views', './views'); // set views folder to our directory
app.set('view engine', 'hbs'); // use our engine

const firebaseApp = firebase.initializeApp(
    functions.config().firebase // use credentials from configured project
);

function getHouses() {
    const ref = firebaseApp.database().ref('houses'); //firebase database
    console.log('inside getHouses');
    return ref.once('value').then(snap => snap.val());
}

// ROUTES
app.get('/all', (request, response) => {
    console.log("showing all houses")
    getHouses().then(house => {
        console.log('returning a house');
        response.render('index', { house }); // render index page and send back data in house var
    });
});

app.get('/hello', (request, response) => {
    console.log("a request came in")
    response.json({message: "Welcome to my API"})
});

// Use with firebase serve command instead of app.listen
// make sure route rewrites are listed in firebase.json
exports.app = functions.https.onRequest(app);

Writing to our Database

To allow our API to write to our database, we’re going to add a one additional dependency. We’re going to install body-parser to allow Express to read the content of our requests. npm i -S body-parser

With it installed, require it into our index.js: const bodyParser = require('body-parser');

And ensure that our app is using it on all routes:

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
Now we’ll construct a POST route to capture our request:
app.post('/new', (request, response) => {
console.log("Making a new house")
const db=firebaseApp.database().ref('houses'); //firebase database
console.log(request.body);
let {name, address, color, rooms, garage} =request.body;
let item= {
"name":name,
"address":address,
"color":color,
"rooms":rooms,
"garage":garage
}
db.push(item); // submit items
response.send(`${name} house created`)
});

We can test this route using Postman by setting our verb to POST, applying a Content-Type header of application/json, and inputting a raw body similar to this:

{ "name":"Steve Rogers' House", "color" : "Red, White, Blue", "address" : "321 Everystreet", "rooms" : "1", "garage" : true}

The final index.js should now look like this:

const functions = require('firebase-functions'); //for firebase storage
const firebase = require('firebase-admin'); //for firebase database
const express = require('express'); // load express from node_modules
const bodyParser = require('body-parser');
const app = express(); // instantiate express to use its functionality
const PORT = process.env.PORT || 3000; // set a port. Look to environment variable if available
app.engine('hbs', engines.handlebars); // use consolidate to attach handlebars templating
app.set('views', './views'); // set views folder to our directory
app.set('view engine', 'hbs'); // use our engine
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));


const firebaseApp = firebase.initializeApp(
    functions.config().firebase // use credentials from configured project
);

function getHouses() {
    const ref = firebaseApp.database().ref('houses'); //firebase database
    console.log('inside getHouses');
    return ref.once('value').then(snap => snap.val());
}

// ROUTES
app.post('/new', (request, response) => {
    console.log("Making a new house")
    const db = firebaseApp.database().ref('houses'); //firebase database
    console.log(request.body);
    let {name, address, color, rooms, garage} = request.body;
    let item = {
        "name": name,
        "address": address,
        "color": color,
        "rooms": rooms,
        "garage": garage
    }
    db.push(item); // submit items
    response.send(`${name} house created`)
});

app.get('/all', (request, response) => {
    console.log("showing all houses")
    getHouses().then(house => {
        console.log('returning a house');
        response.render('index', { house }); // render index page and send back data in house var
    });
});

app.get('/hello', (request, response) => {
    console.log("a request came in")
    response.json({message: "Welcome to my API"})
});

// Use with firebase serve command instead of app.listen
// make sure route rewrites are listed in firebase.json
exports.app = functions.https.onRequest(app);

Congratulations. You now have a working REST API that reads and writes to Firebase!🎉