CRUD with Firestore using the Node.js SDK

justin gage - Sep 21 '20 - - Dev Community

Cloud Firestore is great for building internal apps because it handles all the complicated ins and outs of managing a database for you. In this tutorial, we’ll show you how to set up a basic CRUD app with the Cloud Firestore using the Node.js SDK:

Setting up the Node.js SDK for Cloud Firestore

To get started, you’ll first need install the Node.js client library and initialize your instance of the database. You can install the library via npm with:

npm install firebase-admin --save 
Enter fullscreen mode Exit fullscreen mode

Once the package is installed, you need to initialize the database. You can do this with Firebase Cloud Functions, the Google Cloud Platform, or through your own server. For this tutorial, we’re going to quickly show you how to initialize the database on your own server using a service account, which is an account identity used to make API requests to an application (learn more about service accounts here).

First, you’ll need to go to the Service Accounts menu in Google Cloud Platform account (it’s under IAM & Admin). From there, generate a new private key, save it as a JSON file, and add it to your server.

Then, in your index.js file, include the firebase-admin library and import your service account private key from your server. Use the firebase-admin library to initialize your application, passing an object with credential as the key and firestore.credential.cert() (with your service account as an argument) as the value.

const fs = require('firebase-admin');

const serviceAccount = require('./path/to/key.json');

fs.initializeApp({
 credential: fs.credential.cert(serviceAccount)
});
Enter fullscreen mode Exit fullscreen mode

Finally, call the firestore() amethod to create your database.

const db = fs.firestore(); 
Enter fullscreen mode Exit fullscreen mode

Basic CRUD with the Firestore Node.js SDK

This wouldn’t be a CRUD tutorial without some sample data, so let’s get that out of the way. Imagine we’re building internal tools for an online retailer — let’s say a tool to show customer support reps some user data:

{
 "first": "Liam",
 "last": "Ragozzine",
 "address": "133 5th St., San Francisco, CA",
 "birthday": "05/13/1990",
 "age": "30"
},
{
 "first": "Vanessa",
 "last": "Peluso",
 "address": "49 Main St., Tampa, FL",
 "birthday": "11/30/1977",
 "age": "47"
}
Enter fullscreen mode Exit fullscreen mode

This dataset is, of course, overly simplified - for more complex setups, there are a couple of options for structuring your data in Firestore:

  • Documents
  • Multiple collections
  • Subcollections within documents

Each one has pros and cons that play into ease of use, scalability, and complexity. You can read more about structuring data in Cloud Firestore right here.

Got it? Great! Now for CRUD.

Creating data with set()

To fill our database, we’re going to use the set() method. First, we’re going to specify that we want to fill the users collection with the collection() method. To do this, simply pass the name of the collection into the method as a string:

const usersDb = db.collection('users'); 
Enter fullscreen mode Exit fullscreen mode

Now, we need to specify the first document so we can add our new user, Liam Ragozzine. To do this, we’ll use the doc() method. To use this method, pass the id of the document into doc() as a string. For this example, the id is ‘lragozzine’.

const liam = usersDb.doc('lragozzine'); 
Enter fullscreen mode Exit fullscreen mode

After specifying the document we want to add, we can set the data for the document by passing the data as an object into set().

await liam.set({
 first: 'Liam',
 last: 'Ragozzine',
 address: '133 5th St., San Francisco, CA',
 birthday: '05/13/1990',
 age: '30'
});
Enter fullscreen mode Exit fullscreen mode

Awesome! Now, if we wanted to add our second user, it would look like this:

await usersDb.doc('vpeluso').set({
 first: 'Vanessa',
 last: 'Peluso',
 address: '49 Main St., Tampa, FL',
 birthday: '11/30/1977',
 age: '47'
});
Enter fullscreen mode Exit fullscreen mode

One important thing to note: the set() method will overwrite a document if it already exists. We’ve been using .doc(ID) to create new documents via set(), but if a document with the passed ID already exists, the data you pass in .set() will override whatever currently exists.

Now that we’ve got data in our database, we can move on to reading it.

Reading data with get()

To read your data from Firestore, use the get() method. To read from a collection, specify a collection() before calling get(). Or, if you need to read from a document, specify a doc() before calling get().

// get collection
const users = await db.collection('users').get();

// get document
const liam = await db.collection('users').doc('liam').get();
Enter fullscreen mode Exit fullscreen mode

If the document doesn’t exist, the result will be empty. So in the above example, liam would have no data. You can check this by calling exists on the doc. If the document exists, it will return true and you can call data() to read the data of the document. Otherwise, it will return false.

if (!liam.exists) {
 console.log('No document');
} else {
 console.log(liam.data());
}
Enter fullscreen mode Exit fullscreen mode

To filter (“query”) your results, you can use where(), which takes three arguments:

  1. A field from the collection to test the value of
  2. Relational operator (like <, >, or == for example)
  3. The value for the first argument to be evaluated against

For example, if we wanted all documents for users who are younger than 40, we’d use the following:

const under30 = await
 db.collection('users').where('age', '<=', 40).get();
Enter fullscreen mode Exit fullscreen mode

What’s really cool about Cloud Firestore is that you can get real-time updates on your data using the onSnapshot() method. This lets you listen for changes to any of the documents in your database. After initializing the method and passing a callback function to handle the data, you’ll receive an update every time there’s a change to the content.

const liam = db.collection('users').doc('liam');

const observer = liam.onSnapshot(snapshot => {
 console.log(`changes: ${snapshot}`);
}, err => {
 console.log(`Error: ${err}`);
});
Enter fullscreen mode Exit fullscreen mode

Now, your app is current with the latest trends. 😎

Updating data with set() and update()

In Firestore there are two ways to update data: set() or update(). In short, using set() will generally overwrite the entire document you’re working with, while update() is best for updating particular fields while leaving others untouched.

Starting with set(), if we wanted to make a note in Liam’s document that he is married, we would call set() with the object { married: true }. Like we mentioned above, it’s important to note that if you use set() on a document that already exists, you’ll overwrite it unless you specify merge: true like this:

const liam = await
 db.collection('users').doc('lragozzine').set({
   married: true
 }, { merge: true });
Enter fullscreen mode Exit fullscreen mode

Hint: If you’re not sure whether a document already exists, add merge just in case, so you don’t overwrite your data.

To use the update() method to update a field in a document, pass an object with the field you wish to update as an argument to update().

const liam = await
 db.collection('users').doc('lragozzine').update({
   married: true
 });
Enter fullscreen mode Exit fullscreen mode

To update data in a nested object, you’ll also want to use update(). If we wanted to add an object that contained key-value pairs about a customer’s favorite things (like favorite color, product line, or bad ’90s TV show), we would pass an object where the key is a path, like this:

const liam = await
 db.collection('users').doc('lragozzine').update({
   'favorites.item': 'Ties'
 });
Enter fullscreen mode Exit fullscreen mode

Now that we know how to merge and update our documents, let’s move on to deleting.

Deleting data with delete()

This shouldn’t be a surprise: you delete data from Firestore using the delete() method. It’ll look something like this:

await db.collection('users').doc('lragozzine').delete(); 
Enter fullscreen mode Exit fullscreen mode

This will delete Liam’s entire document from the users database.

Note: If you delete a document, it will not delete the document’s subcollections. So if Liam had a subcollection in his document that contained documents with order data, called orders, then even after deleting the lragozzine document, we would still be able to access the subcollection orders, like this:

const liamOrders = await db.collection('users').doc('lragozzine')
 .collection('orders').doc('123').get();
Enter fullscreen mode Exit fullscreen mode

To delete a field, use FieldValue.delete() in an update() command. First, you must import the FieldValue object, and then call delete() on it, like this:

const FieldValue = fs.firestore.FieldValue;

const r = await
 db.collection('users').doc('lragozzine').update({
   married: FieldValue.delete();
 });
Enter fullscreen mode Exit fullscreen mode

Deleting entire collections gets a little more complicated because you have to make requests to retrieve all documents in a collection and delete them one by one. For more information on deleting collections, check out Firebase’s guide.

Congratulations! You’re now a Cloud Firestore CRUD expert.

. . . . . . . . . . . . . . . . . . . . . . . .