CRUD with Firestore using the Node.js SDK
Tags: firebase, firestore, webdev, node
Published: 2020-09-21 14:07:13

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:

* Set up the Cloud Firestore Node.js SDK
* <a ahref="#createdata">Create data</a>
* <a href="#readdata">Read data</a>
* <a href="#updatedata">Update data</a>
* <a href="#deletedata">Delete data</a> 

## Setting up the Node.js SDK for Cloud Firestore
<br>

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](https://www.npmjs.com/) with:

```
npm install firebase-admin --save 
```

Once the package is installed, you need to [initialize the database](https://firebase.google.com/docs/firestore/quickstart#initialize). You can do this with [Firebase Cloud Functions](https://firebase.google.com/products/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](https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances), which is an account identity used to make API requests to an application (learn more about service accounts [here](https://cloud.google.com/compute/docs/access/service-accounts#serviceaccount)).

First, you’ll need to go to the Service Accounts menu in Google Cloud Platform account (it’s under [IAM & Admin](https://console.cloud.google.com/projectselector2/iam-admin/serviceaccounts?pli=1&supportedpurview=project)). 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)
});
```

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

```
const db = fs.firestore(); 
```

## Basic CRUD with the Firestore Node.js SDK
<br>

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"
}
```

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](https://firebase.google.com/docs/firestore/manage-data/structure-data).

Got it? Great! Now for CRUD.

<br>

<a name="createdata"></a>
### Creating data with `set()`
<br>

To fill our database, we’re going to use the [`set()` method](https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document). 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'); 
```

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](https://firebase.google.com/docs/firestore/manage-data/add-data#add_a_document). 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'); 
```

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'
});
```

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'
});
```

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.

<br>

<a name="readdata"></a>
### Reading data with `get()`
<br>

To read your data from Firestore, use the [`get()` method](https://firebase.google.com/docs/firestore/query-data/get-data#get_a_document). 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();
```

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());
}
```

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();
```

What’s really cool about Cloud Firestore is that you can get real-time updates on your data using the [`onSnapshot()` method](https://firebase.google.com/docs/firestore/query-data/listen). 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}`);
});
```

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

<br>

<a name="updatedata"></a>
### Updating data with `set()` and `update()`
<br>

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 });
```

*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](https://firebase.google.com/docs/firestore/manage-data/add-data#update-data) 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
 });
```

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'
 });
```

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

<br>

<a name="deletedata"></a>
### Deleting data with `delete()`
<br>

This shouldn’t be a surprise: you delete data from Firestore using the [`delete()` method](https://firebase.google.com/docs/firestore/manage-data/delete-data#delete_documents). It’ll look something like this:

```
await db.collection('users').doc('lragozzine').delete(); 
```

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();
```

To [delete a field](https://firebase.google.com/docs/firestore/manage-data/delete-data#fields), 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();
 });
```

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](https://firebase.google.com/docs/firestore/solutions/delete-collections).

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