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.
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
.
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.
You can download the full source code of this example here: Firebase Cloud Firestore using Node.js