CRUD with Cloud Firestore and Node.js by Google Cloud Function

2022/10/066 min read
bookmark this
Responsive image

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Setup npm Repository
  4. Adding Code to Interact with Firestore
  5. Prepare the CRUD Operations
  6. Read Data - HTTP GET
  7. Create Data - HTTP POST
  8. Update Data - HTTP PUT
  9. Delete Data - HTTP DELETE
  10. Complete index.js File
  11. Deploy to the Cloud
  12. Conclusion

Introduction

This blog focuses on how to use Google Cloud Functions to handle CRUD operations with Cloud Firestore. Cloud Functions act as the HTTP server, while Firestore serves as the NoSQL database backend. Both services are serverless and hosted in the cloud, so you can focus on your business logic without worrying about server management.

Prerequisites

Before we start, you will need to prepare the following:

Cloud Functions to Firestore

  • A GCP (Google Cloud Platform) account
  • A GCP project set up
  • A Firestore collection configured in your GCP project
  • A service account with the Cloud Datastore Owner role, with the key file downloaded to your local repository folder

Setup npm Repository

First, initialize the npm repository by running the following command. This will create a package.json file with default settings.

npm init -y

Next, install @google-cloud/functions-framework so you can test the Cloud Function locally.

npm install --save-dev @google-cloud/functions-framework

To test locally, add the following script to your package.json:

"start": "npx functions-framework --target=main [--signature-type=http]"

Also, install the firebase-admin package so you can communicate with Firebase and Firestore.

npm install --save firebase-admin

Adding Code to Interact with Firestore

Once the npm packages are installed, we can start adding code. Create a file called index.js and add the following code to initialize Firestore with the service account credentials.

const { initializeApp, cert } = require("firebase-admin/app");
const { getFirestore } = require("firebase-admin/firestore");

const serviceAccount = require("./service-account-has-fireStore-role.json");
initializeApp({
  credential: cert(serviceAccount),
});

Prepare the CRUD Operations

We will create the following basic CRUD operations from the Cloud Function to the Firestore database:

  • Read Data - [HTTP GET] - http://localhost:8080/?docid=800
  • Create Data - [HTTP POST] - http://localhost:8080/
{
  "docid": "2000",
  "id": "test_10",
  "list": [1, 2, 3]
}
  • Update Data - [HTTP PUT] - http://localhost:8080/?docid=800
{
  "id": "test_10",
  "list": [1, 2, 3]
}
  • Delete Data - [HTTP DELETE] - http://localhost:8080/?docid=800

Read Data - HTTP GET

First, we handle the HTTP GET request for the read operation. Here we check that the HTTP method is GET and that the docid query string parameter is provided.

exports.main = (req, res) => {

if (req.method === 'GET') {
    if (!req.query.docid) {
        return res.status(404).send('');
    }

    db.collection('my_collection').doc(req.query.docid.toString()).get()
    .then(doc => {
        return res.status(200).send(doc.data());
    });
}

Below is how it looks when we run npm run start and test with Postman. Cloud Functions to Firestore

Create Data - HTTP POST

For creating data, we expect the HTTP method to be POST and the request body to contain the following JSON structure:

{
  "docid": "200",
  "id": "test_10",
  "list": [10, 20, 30, 40]
}

Once we receive the request.body, we use Firestore's set function to create or update the document in the target collection.

exports.main = (req, res) => {

    else if (req.method === 'POST') {

        db.collection('my_collection')
        .doc(req.body.docid)
        .set({
            id: req.body.id,
            list: req.body.list
        })
        .then((doc) => {
            return res.status(200).send(doc);
        });
    }

Below is how it looks when we run npm run start and test with Postman. HTTP Post with Firestore

Update Data - HTTP PUT

For the update operation, we continue using the set function with the document ID passed as a query string parameter.

exports.main = (req, res) => {

    else if (req.method === 'PUT') {
        db.collection('my_collection')
        .doc(req.query.docid)
        .set({
            id: req.body.id,
            list: req.body.list
        })
        .then((result) => {
            return res.status(200).send(result);
        });
    }

Below is how it looks when we run npm run start and test with Postman. HTTP Put with Firestore

Delete Data - HTTP DELETE

For the delete operation, we expect the docid to be passed as a query string parameter and use the delete method to remove the document from the Firestore database.

exports.main = (req, res) => {

    else if (req.method === 'DELETE') {
        db.collection('my_collection')
        .doc(req.query.docid).delete()
        .then((result) => {
            return res.status(200).send(result);
        });
    }

Below is how it looks when we run npm run start and test with Postman. HTTP Delete with Firestore

The Complete index.js File for CRUD Operations

The following snippet contains all the CRUD code mentioned earlier in a single file.

const {
  initializeApp,
  applicationDefault,
  cert,
} = require("firebase-admin/app");
const {
  getFirestore,
  Timestamp,
  FieldValue,
} = require("firebase-admin/firestore");

const serviceAccount = require("./none-firebase-admin-firm-champion-365105-f0d88d6192e2.json");

initializeApp({
  credential: cert(serviceAccount),
});

const db = getFirestore();

exports.main = (req, res) => {
  if (req.method === "GET") {
    if (!req.query.docid) {
      return res.status(404).send("");
    }

    db.collection("my_collection")
      .doc(req.query.docid.toString())
      .get()
      .then((doc) => {
        return res.status(200).send(doc.data());
      });
  } else if (req.method === "POST") {
    db.collection("my_collection")
      .doc(req.body.docid)
      .set({
        id: req.body.id,
        list: req.body.list,
      })
      .then((doc) => {
        return res.status(200).send(doc);
      });
  } else if (req.method === "PUT") {
    db.collection("my_collection")
      .doc(req.query.docid)
      .set({
        id: req.body.id,
        list: req.body.list,
      })
      .then((result) => {
        return res.status(200).send(result);
      });
  } else if (req.method === "DELETE") {
    db.collection("my_collection")
      .doc(req.query.docid)
      .delete()
      .then((result) => {
        return res.status(200).send(result);
      });
  }
};

Deploy Node.js Code to the Cloud

For deployment, you can use gcloud to deploy from your local machine, integrate with CI/CD, or deploy from a cloud bucket as a zip file. In this example, we will deploy directly from your local folder to the cloud.

Below is the sample command that deploys the function named my-firestore-function to Google Cloud Functions:

gcloud functions deploy my-firestore-function --trigger-http --region=us-central1 --runtime=nodejs16 --gen2 --allow-unauthenticated --entry-point=main

Once the deployment is complete, you should see a URL in the console output. You can also find your Cloud Function URL in the GCP Console. If you browse to the URL with the docid query string parameter, you should see a JSON response from the Firestore database.

Conclusion

This guide demonstrated how to use Google Cloud Functions as an HTTP server to perform CRUD operations with Cloud Firestore as the backend database. Key takeaways:

  1. Cloud Functions handle HTTP requests (GET, POST, PUT, DELETE) for each CRUD operation
  2. Firestore's set method can be used for both creating and updating documents
  3. Local testing is possible using the functions-framework before deploying to the cloud
  4. Both Cloud Functions and Firestore are serverless, so you can focus on your business logic without managing servers