JavaScript

Firebase Cloud Firestore using Node.js

Hello. In this tutorial, we will explain the implementation of Firebase Firestore in the node.js application. The application will be responsible to perform the crud operations.

1. Introduction

Firebase is a Backend-as-a-Service (BaaS) that helps to store real-time data in the database. Allows syncing the real-time data across all devices. Features –

  • Serverless
  • Highly secure
  • Offers minimal setup
  • Provides three-way binding via angular fire
  • Easily access data, authentication, and more
  • Offers JSON storage that means no barrier between data and objects
  • Has a good storage potential

Just like any other storage solution firebase also have some advantages i.e.

  • Limited querying and indexing support
  • No support for aggregation and map-reduce functionality

1.1 Setting up Node.js

To set up Node.js on windows you will need to download the installer from this link. Click on the installer (also include the NPM package manager) for your platform and run the installer to start with the Node.js setup wizard. Follow the wizard steps and click on Finish when it is done. If everything goes well you can navigate to the command prompt to verify if the installation was successful as shown in Fig. 1.

Firebase Firestore in node.js - verifying npm installation
Fig. 1: Verifying node and npm installation

2. Firebase Cloud Firestore using Node.js

To set up the application, we will need to navigate to a path where our project will reside. For programming stuff, I am using Visual Studio Code as my preferred IDE. You’re free to choose the IDE of your choice.

2.1 Setting up the Firebase project

To set up the project implementation we will need a firebase project with the real-time database enabled. Once the project is created copy the firebase configuration which will be used in the .env properties. Refer to Section 2.2.2 for details. You can take a look at this link to quickly create a firebase project in the google console.

2.2 Setting up the implementation

Let us write the different files which will be required for practical learning.

2.2.1 Setting up dependencies

Navigate to the project directory and run npm init -y to create a package.json file. This file holds the metadata relevant to the project and is used for managing the project dependencies, script, version, etc. Add the following code to the file wherein we will specify the required dependencies.

package.json

{
  "name": "node-firebase",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "cors": "^2.8.5",
    "dotenv": "^10.0.0",
    "express": "^4.17.1",
    "firebase": "^8.0.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  }
}

To download the dependencies navigate to the directory path containing the file and use the npm install command. If everything goes well the dependencies will be loaded inside the node_modules folder and you are good to go with the further steps.

2.2.2 Creating an environment file

Add the following code to the environment file. The file will be responsible to have the configuration variables spread across the application for further use.

.env

#express server configuration
PORT=3000
HOST=localhost
HOST_URL=http://localhost:3001

#firebase configuration
API_KEY=your_api_key
AUTH_DOMAIN=app_auth_domain
DATABASE_URL=app_realtime_database_url
PROJECT_ID=app_id
STORAGE_BUCKET=app_storage_bucket
MESSAGING_SENDER_ID=messaging_sender_id
APP_ID=app_id

2.2.3 Creating configuration file

Add the following code to the configuration file. The file will be responsible to import the environment variables from the .env file and perform the initialization assertions. The assertions to helps to avoid the common failures post the application startup.

config.js

const dotenv = require("dotenv");
const assert = require("assert");

dotenv.config();

const {
  PORT,
  HOST,
  HOST_URL,
  API_KEY,
  AUTH_DOMAIN,
  DATABASE_URL,
  PROJECT_ID,
  STORAGE_BUCKET,
  MESSAGING_SENDER_ID,
  APP_ID
} = process.env;

// adding init assertions
assert(PORT, "Application port is required");
assert(HOST_URL, "Service endpoint is required");
assert(DATABASE_URL, "Firebase database endpoint is required");
assert(PROJECT_ID, "Firebase project id is required");
assert(APP_ID, "Firebase app id is required");

module.exports = {
  port: PORT,
  host: HOST,
  url: HOST_URL,
  firebaseConfig: {
    apiKey: API_KEY,
    authDomain: AUTH_DOMAIN,
    databaseURL: DATABASE_URL,
    projectId: PROJECT_ID,
    storageBucket: STORAGE_BUCKET,
    messagingSenderId: MESSAGING_SENDER_ID,
    appId: APP_ID
  }
};

2.2.4 Creating database configuration

Add the following code to the database configuration file. The file will be responsible to initialize the Firebase configuration required for this application.

db.js

const firebase = require("firebase");
const config = require("./config");

const db = firebase.initializeApp(config.firebaseConfig);

module.exports = db;

2.2.5 Creating a model class

Add the following code to the model class which will be responsible to map the data to/from the Firebase Firestore collection.

employee.js

class Employee {
  constructor(id, fullName, age, contact, department) {
    this.id = id; // represents the id generated by the firestore
    this.fullName = fullName;
    this.age = age;
    this.contact = contact;
    this.department = department;
  }
}

module.exports = Employee;

2.2.6 Creating a controller class

Add the following code to the controller class which will be responsible to get the data from the client and interact with the Firebase Firestore collection. The class consists of different methods to save a new employee, update/delete/get an employee by id, and get all employees.

employeeController.js

const firebase = require("../db");
const Employee = require("../models/employee");
const fireStore = firebase.firestore();

// performing crud operations in the firebase firestore
// add
// get all
// get
// update
// delete

const addEmployee = async (req, res, next) => {
  try {
    console.log("Adding new employee");
    const data = req.body;
    await fireStore.collection("employees").doc().set(data);
    res.status(201).json({ message: "Record saved successfully" });
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
};

const getAllEmployees = async (req, res, next) => {
  try {
    console.log("Getting all employees");
    const employees = await fireStore.collection("employees");
    const data = await employees.get();
    const arr = [];
    if (data.empty) {
      res.status(200).json({ message: "No records found" });
    } else {
      let total = 0;
      data.forEach((item) => {
        const employee = new Employee(
          item.id,
          item.data().fullName,
          item.data().age,
          item.data().contact,
          item.data().department
        );
        arr.push(employee);
        total = total + 1;
      });
      res.status(200).json({
        listing: arr,
        count: total
      });
    }
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
};

const getEmployee = async (req, res, next) => {
  try {
    const id = req.params.id;
    console.log("Getting employee= %s", id);
    const employee = await fireStore.collection("employees").doc(id);
    const data = await employee.get();
    if (!data.exists) {
      res.status(404).json({ message: "Record not found" });
    } else {
      res.status(200).json(data.data());
    }
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
};

const updateEmployee = async (req, res, next) => {
  try {
    const id = req.params.id;
    console.log("Updating employee= %s", id);
    const data = req.body;
    const employee = await fireStore.collection("employees").doc(id);
    await employee.update(data);
    res.status(204).json({ message: "Record updated successfully" });
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
};

const deleteEmployee = async (req, res, next) => {
  try {
    const id = req.params.id;
    console.log("Deleting employee= %s", id);
    await fireStore.collection("employees").doc(id).delete();
    res.status(204).json({ message: "Record deleted successfully" });
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
};

// todo - add delete all employees

module.exports = {
  addEmployee,
  getAllEmployees,
  getEmployee,
  updateEmployee,
  deleteEmployee
};

2.2.7 Creating a router class

Add the following code to the router class which will be responsible to map the incoming request from the client with the appropriate controller method.

employeeController.js

const express = require("express");
const {
  addEmployee,
  getAllEmployees,
  getEmployee,
  updateEmployee,
  deleteEmployee
} = require("../controllers/employeeController");

const router = express.Router();

// http://localhost:3000/api/employee
router.post("/employee", addEmployee);

// http://localhost:3000/api/employees
router.get("/employees", getAllEmployees);

// http://localhost:3000/api/employee/xxxx_employee_id
router.get("/employee/:id", getEmployee);

// http://localhost:3000/api/employee/xxxx_employee_id
router.put("/employee/:id", updateEmployee);

// http://localhost:3000/api/employee/xxxx_employee_id
router.delete("/employee/:id", deleteEmployee);

module.exports = {
  routes: router
};

2.2.8 Creating an index file

Add the following code to the index file which will be responsible for the application startup and act as a welcome point for the application.

index.js

const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const config = require("./config");
const employeeRoutes = require("./routes/employeeRoutes");

const app = express();

app.use(express.json());
app.use(cors());
app.use(bodyParser.json());

app.use("/api", employeeRoutes.routes);

app.listen(config.port, () => {
  console.log("Service endpoint= %s", config.url);
});

3. Run the Application

To run the application navigate to the project directory and enter the following command as shown in Fig. 2. If everything goes well the application will be started successfully on a port number read from the .env file. In this case, the application will be started successfully on a port number – 3001.

Fig. 2: Starting the application

4. Demo

You are free to use postman or any other tool of your choice to make the HTTP request to the application endpoints.

Endpoints

// add an employee
// http post
http://localhost:3000/api/employee

// get all employees
// http get
http://localhost:3000/api/employees

// get an employee
// http get
http://localhost:3000/api/employee/xxxx_employee_id

// update an employee
// http put
http://localhost:3000/api/employee/xxxx_employee_id

// delete an employee
// http delete
http://localhost:3000/api/employee/xxxx_employee_id

That is all for this tutorial and I hope the article served you with whatever you were looking for. Happy Learning and do not forget to share!

5. Summary

In this tutorial, we learned about integrating Firebase in the nodejs application. Firebase offers a real-time database that offers the data to be stored in a JSON format and offers a fast way to implement the BaaS solution. You can download the source code and the postman collection from the Downloads section.

6. Download the Project

This was a tutorial to integrate Firebase in the nodejs application.

Download
You can download the full source code of this example here: Firebase Cloud Firestore using Node.js

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button