Node Firestore Import

Share on:

A quick aide memoir on how to import data from a CSV formatted files into Firestore. If you have used the Pet Theory lab, this should be familar, but if not here is the code.

The general idea is to create a csv and import it directly into Cloud Firestore. To do this, the code will read the csv, then connect to Firestore and add a new collection containing the csv records.

If you havent worked with Firestore before, this is a nice short cut to get to grips with the Firestore API.

Requirements:

  • Node.js installation
  • Firestore package

Overview

In this blog post learn how to parse a CSV, and add the information to Firestore. The following content can also be applied to a different formats if required.

  • To transition between JSON and CSV, check out the JQ to CSV blog post.
  • To create fake CSV test data, check out the Faker blog post.

Run app

The npm packages are required:

1npm install csv-parse @google-cloud/firestore

Run the application

Add the csv file e.g. customers_10.csv as the input.

1node index.js customers_10.csv 

Expected Output:

1Wrote 10 records

Refresh the Cloud Firestore database to see the updated records.

Application Code

The template application will be based on Node.js. If you are unfamiliar with Node.js, this is a standard step to create the files required.

The package.json file provides the information relating to packages used and general descriptions. In addition it has a helpful script section, for testing that can be generalised.

  1. Add a package.json
 1{
 2  "name": "fs-import",
 3  "version": "1.0.0",
 4  "description": "Import CSV into Cloud Firestore",
 5  "main": "index.js",
 6  "scripts": {
 7    "test": "echo \"Error: no test specified\" && exit 1"
 8  },
 9  "keywords": [],
10  "author": "Rich Rose",
11  "license": "MIT",
12  "dependencies": {
13    "@google-cloud/firestore": "^7.3.0",
14    "csv-parse": "^5.5.3",
15  }
16}
  1. Create an index.js file

The code is required to do the following things:

  • Include package dependencies
  • Add validation for command line arguments
  • Add a writeToFirestore function
  • Add an importCSV function

Package Dependencies

A couple of declarations are added for handling the data entry. The npm packages are required:

  • csv-parse
  • @google-cloud/firestore
 1const csv = require('csv-parse');
 2const fs  = require('fs');
 3const { Firestore } = require("@google-cloud/firestore");
 4
 5const projectId    = 'PROJECT_ID'
 6const collectionId = 'customers'
 7
 8const resource = {
 9  type: "global",
10};
11

Validate Command Line Arguments

The application expects the name of a CSV file to be added. If a filename is not present, the application will exit.

1if (process.argv.length < 3) {
2  console.error('Please include a path to a csv file');
3  process.exit(1);
4}
5
6importCsv(process.argv[2]).catch(e => console.error(e));

Read the CSV file

Use the csv-parse package to read a csv filename. The csv is comma separated and is expected to include a header. Each item read becomes a record with the header values used as field names.

 1async function importCsv(csvFilename) {
 2  const parser = csv.parse({ columns: true, delimiter: ',' }, async function (err, records) {
 3    if (err) {
 4      console.error('Error parsing CSV:', err);
 5      return;
 6    }
 7    try {
 8      console.log(`Call write to Firestore`);
 9      await writeToFirestore(records);
10      console.log(`Wrote ${records.length} records`);
11    } catch (e) {
12      console.error(e);
13      process.exit(1);
14    }
15  }); 
16
17  await fs.createReadStream(csvFilename).pipe(parser);
18}

Write to Firestore

The writeToFirestore function is asynchronous and reads each record presented. The record parameter is a data object, and each item is written to the named collection.

Note:

  • The code does not include pagination, so loading of huge files is not supported.
  • Assumes the application code can communicate to Cloud Firestore database.
 1async function writeToFirestore(records) {
 2  const db = new Firestore({  
 3    // projectId: projectId 
 4  });
 5  const batch = db.batch()
 6
 7  records.forEach((record)=>{
 8    console.log(`Write: ${record}`)
 9    const docRef = db.collection(collectionId).doc(record.email);
10    // batch.create(docRef, record)
11    batch.set(docRef, record, { merge: true })
12  })
13
14  batch.commit().then(() => {
15    console.log('Batch executed')
16  }).catch(err => {
17    console.log(`Batch error: ${err}`)
18  })
19  return 
20}

Complete Code

 1const fs  = require('fs');
 2const csv = require('csv-parse');
 3const { Firestore } = require("@google-cloud/firestore");
 4
 5const projectId    = 'PROJECT_ID'
 6const collectionId = 'customers'
 7
 8const resource = {
 9  type: "global",
10};
11
12async function writeToFirestore(records) {
13  const db = new Firestore({  
14//        projectId: projectId 
15  });
16  const batch = db.batch()
17
18  records.forEach((record)=>{
19    console.log(`Write: ${record}`)
20    const docRef = db.collection(collectionId).doc(record.email);
21    // Create new document
22    // batch.create(docRef, record)
23    // Merge or create new doc
24    batch.set(docRef, record, { merge: true})
25  })
26
27  batch.commit().then(() => {
28    console.log('Batch executed')
29  }).catch(err => {
30    console.log(`writeToFirestore: ${err}`)
31  })
32  return 
33}
34
35async function importCsv(csvFilename) {
36  const parser = csv.parse({ columns: true, delimiter: ',' }, async function (err, records) {
37    if (err) {
38      console.error('csv-parse:', err);
39      return;
40    }
41    try {
42      console.log(`Call write to Firestore`);
43      await writeToFirestore(records);
44      console.log(`Wrote ${records.length} records`);
45    } catch (err) {
46      console.error(`importCsv: ${err}`);
47      process.exit(1);
48    }
49  }); 
50
51  await fs.createReadStream(csvFilename).pipe(parser);
52}
53
54if (process.argv.length < 3) {
55  console.error('Please include a path to a csv file');
56  process.exit(1);
57}
58
59importCsv(process.argv[2]).catch(err => console.error(err));