3.2 Getting serious with Firebase V9 - Using the Firebase emulators
Tags: javascript, beginners, firebase
Published: 2021-12-02 10:25:08

Last reviewed : May 2023

### Introduction ###

When part 3.1 of this series ([Moving to "ECMA Modules"](https://dev.to/mjoycemilburn/getting-serious-with-javascript-development-and-firebase-v9-part-1-moving-to-ecma-javascript-modules-2b7p)) introduced you to the Firebase V9 modular platform, it left you working exclusively into your production Cloud database.

This is fine while you're getting ready for your first live release, but once you've got real users and have changes to test out you need to think again.

What you need is some way of doing your testing locally. But just thinking about how you'd go about this, even with the limited range of Firebase services you've seen so far - database and rules - is probably enough to make you go weak at the knees.

Local testing is never easy to organise. In the past you would have had to sort something out entirely under your own steam - for example, you might have had to install a local SQL server. This would be hard work and would likely create some dangerous inconsistencies due to differences between your test tools and their live equivalents.

The good news is that the Firebase emulator suite creates a near-perfect local copy of Firebase production arrangements. For example, when you launch the **Firestore** emulator, you'll find that it looks almost **exactly** like the live version in the Firebase Console.

So, lets get started. As a fallback, if I'm not making things clear enough, you might find it useful to refer to Google's own documentation at [Install, configure and integrate Local Emulator Suite ](https://firebase.google.com/docs/emulator-suite/install_and_configure).

### Configuring the Firebase emulators ###

If you've previously read [Project Configuration](https://dev.to/mjoycemilburn/a-beginners-guide-to-javascript-development-using-firebase-v9-part-1-project-configuration-5bo3) in this series, you'll find that you've actually been here before. Configuring the Firebase emulators just requires another run through `Firebase init` to add a few more parameters to the `firebase.json` file. So, open a terminal window for your project and enter the following command:
```
firebase init emulators
```
Once you've confirmed that **yes** you **do** want to proceed, the CLI will ask you which emulators you want to use.

Because terminal windows don't respond to "point and click" instructions, selecting your emulators from the list presented is a rather cumbersome procedure. You have to proceed by using the arrow keys on your keyboard. I suggest that, for now, you concentrate on just the "Firestore" and "Hosting" emulators - ie those for which you have an immediate requirement.

So, press the down-arrow key to highlight the corresponding entries in the list and, for each, select the entry by pressing the space bar (this toggles selection on and off). Once you're done, activate your request by pressing the return key.

Accept the "default ports" offered by the CLI and also respond "y" to the offer to "download the emulators now?". The CLI should then respond with "Firebase initialization complete!".

You might find it interesting now to see what the CLI has done to your `firebase.json` file. This should now look something like the following:
```
{
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  },
  "emulators": {
    "firestore": {
      "port": 8080
    },
    "hosting": {
      "port": 5000
    },
    "ui": {
      "enabled": true
    }
  }
}
```
You should now be able to start the emulators as follows:
```
firebase emulators:start
```
In response you'll see something like:
```
i  emulators: Starting emulators: firestore, hosting
!  emulators: It seems that you are running multiple instances of the emulator suite for project fir-expts-app. This may result in unexpected behavior.
i  firestore: Firestore Emulator logging to firestore-debug.log
i  hosting: Serving hosting files from: public
+  hosting: Local server: http://localhost:5000
!  ui: Emulator UI unable to start on port 4000, starting on 4001 instead.
i  ui: Emulator UI logging to ui-debug.log

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://localhost:4001                │
└─────────────────────────────────────────────────────────────┘

┌───────────┬────────────────┬─────────────────────────────────┐
│ Emulator  │ Host:Port      │ View in Emulator UI             │
├───────────┼────────────────┼─────────────────────────────────┤
│ Firestore │ localhost:8080 │ http://localhost:4001/firestore │
├───────────┼────────────────┼─────────────────────────────────┤
│ Hosting   │ localhost:5000 │ n/a                             │
└───────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at localhost:4400
  Other reserved ports: 4500

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.  
  ```
You may be alarmed at this point to find that your terminal session appears to have stalled. Fret not. What has happened is that your terminal session has become a "server" for your emulator components. It's now waiting for you to start using them.

To see what's going on, cut and paste the address displayed above as View Emulator UI at `http://localhost:4001` into your browser to open the following page:
![Emulator launchpad](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8kt9wxkqhg4rmd3basl2.png)

What you've seeing here is a sort of "launchpad" for the various emulator components. Just for the moment, concentrate on the "Firestore emulator" in the middle of the top row and the "Hosting emulator" to the right of the second row. The first of these launches a tool that allows you to create and administer a local Firestore database and the second gives you a quick way of starting your `index.html` file in the browser. 

Starting with the "Firestore emulator" box, click the "Go to emulator" button at the bottom right. This will reveal a "Firestore Emulator Suite" page" that should look a bit familiar.
![Firestore emulator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s6cee2k9ho9qc6fn2fhi.png)
Yes, this is a fair replica of the procedure you used to create test data in the early days when you were using the Firestore tool in the Cloud Firebase Console.

Returning to the "launchpad" page and the "Hosting emulator" box, click the "View website" button to the bottom right. This will "serve" your `index.html` file as "localhost" in your browser.

![Webpage launched from Firebase emulator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/56byc2oowiak9nrzw2nj.png)
Take a moment to consider what's happening here. Your `index.html` file has been launched directly into the browser with its Firebase code in whatever state you last left it. For example, if you have now moved onto modular imports, it will be referencing the `packed_index.js` file you created with `webpack`. Even better, it runs! **But**, this locally hosted webapp is still connecting to your Cloud Firestore collections. We're still not quite where we want to be

### Creating and operating a local Firestore database ###

You've now given yourself a way of testing code changes without deploying them into the live system, but this is no help if these changes make a mess of your live data. So, you now need a way to redirect the local code to a local database. 

**Connecting your webapp to a local database**

This is surprisingly easy to do, though it does require you to make a change to your webapp's code. Just add the following line of code to the `index.js` file after you've set up the `db` variable:
```
connectFirestoreEmulator(db, 'localhost', 8080);
```
and add the `connectFirestoreEmulator` function to your `firestore` import

But now the question is "how do you get some data into this local database?"

**Creating and populating a local database**

Although you've already seen that the emulator gives you a familiar arrangement for defining and populating collections, you may still have grim memories of trying to do this through the live Firebase console. It's not going to be any easier here. In such a situation you'll most likely be looking for a Firebase export/import facility to haul a suitable dataset out of your **live** database. 

Sadly, I'm afraid that, at least at the time of writing (Nov 2021), you will look in vain. There **is** a Cloud database export facility but it's designed for Cloud backup and it's not going to be at all helpful in providing you with a local extract file. 

While you're still getting your eye in, I suggest you just add a temporary bit of initialisation code to your application. You can easily comment it out when you want to revert your code for live operation.  Here's an example of what you might write:
```javascript
async function buildTestData() {
    let collectionData = [
        {
            "userEmail": "ab@gmail.com",
            "userPurchase": "chocolate"
        },
        {
            "userEmail": "ab@gmail.com",
            "userPurchase": "firelighters"
        }
    ]


    for (let i = 0; i < collectionData.length; i++) {
        let collRef = collection(db, "userShoppingLists");
        let docRef = doc(collRef);
        await setDoc(docRef, collectionData[i]).catch((error) => {
            alert("Error in buildTestData " + error)
        });
    } 
}
```
It shouldn't be too difficult to invent ways of obtaining more elaborate jsons from your production collections. For example you might add a hidden button to your live webapp to print out a json that you can then copy and paste into your code via the clipboard.

If you're still determined to find more serious ways of importing a collection from your production Cloud Firestore database, you might look at [How to import data from cloud firestore to the local emulator?](https://stackoverflow.com/questions/57838764/how-to-import-data-from-cloud-firestore-to-the-local-emulator) on Stackoverflow.

**Building a library of Test data sets**

Once you've finally succeeded in your bid to create test collections in your local database, you are likely to feel distinctly unhappy when you restart the emulators and find that your data has disappeared! Yes, unless directed otherwise, the Firestore emulator always starts with a clean sheet.

Actually, when you think about it, this is quite a sensible idea. When you're doing serious testing, you'll want your tests start with a clean database served up from a standard data set.

To ask the emulator to **save** your data collections after you've just created them you just issue a one-off command like:
```
firebase emulators:export ./[collection_backup]
```
where [collection_backup] is a folder name that you choose yourself. If the [collection_backup] folder doesn't exist, firebase will create it. 

To restart the emulators with a copy of this data you would then use the following command:
```
firebase emulators:start --import=./[collection_backup]
```
Management of this arrangement for a serious project is clearly going to be an issue,- I'm sure that by now you'll have realised that you need to be quite organised if you're to use the emulators effectively. 

Precise arrangements beyond this are clearly going to differ from project to project. But you probably need to:

1. Plan for an initial test-collection initialisation phase to set up test data sets in a series of [collection_backup] folders.
2. Perhaps consider creating a corresponding series of "run_emulation_for_[collection_backup].ps1" scripts to systematise the running of a particular test against the correct data set.
3. Plan procedures to ensure that the code changes introduced to rig your source code for testing can't leak into the live system. These arrangements will obviously need to fit into whatever procedures you choose to adopt for version management and are beyond the scope of this particular post.

Yes, serious IT is hard!

**Applying Rules in the Emulation environment**

You'll note that while the "Firestore Emulator Suite" page offered facilities to manage **collections** it said nothing about **rules**. In the emulation environment you need to specify these separately in a `firestore.rules` file (stored at the project root) and provide a pointer to this file in your `firebase.json`. This is easy enough to organise - just cut and paste from your live rules page into your `firestore.rules` file and add the following to your firebase.json:
```
    "firestore": {
      "rules": "firestore.rules"
    },
```
To run the example code provided in [Coding a simple webapp](https://dev.to/mjoycemilburn/a-beginners-guide-to-javascript-development-using-firebase-v9-part-2-coding-a-simple-webapp-4e5i/edit) - the final version with a user-login - my firestore.rules file contained the following:
```
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /userShoppingLists/{document} {
    	allow read, write : if true;
  	}
  }
}
```
Note that when you switch back to production deployment, a bare `firebase deploy` command will upload your firestore.rules file along with everything else to the Cloud. If subsequently you use the Firestore console to make changes directly into the hosted system, it's quite easy to forget that any subsequent `firebase deploy` wil overwrite these changes with the content of `firestore.rules` - you need to change these too.

The next post in this series [Background processing with Cloud Functions](https://dev.to/mjoycemilburn/getting-serious-with-firebase-v9-part-3-background-processing-with-cloud-functions-2di7) describes Firebase facilities for handling processing requirements that aren't natural candidates for browser operation. You'll find that the skills you've acquired with the emulators come in very handy here because they make development in this area a whole lot easier. 

### Appendix- using VSCode terminal windows ###

If you're unfamiliar with VSCode terminal window, here are a few notes that you might find useful:

1. To open a new terminal window in VSCode, click "terminal/new terminal" on the menu bar and select the folder (project) that you want the session to focus on. You can open multiple windows and, in fact, you will **need** to open multiple terminals because once you've started the Firebase **emulators** in a terminal window this will be locked to their server. Meanwhile, you will still need to issue other commands (eg `firebase emulators:export ./collection_backup` to backup a collection). You can toggle between terminal sessions using the drop-down list at the head of the terminal window. If you've hidden all your terminal windows, you can reopen the latest one by clicking "view/terminal". 

2. When you start a new terminal it will be opened with the default shell (powershell is a popular choice), but once you've started the emulators, you'll find the terminal type has switched from "powershell" to "node". This is because the original terminal session has started a `node` server to deliver the emulator functions. To stop the emulator, use ctrl C, but note that this will likely leave a java service running and clinging onto a port. You can clear this with the command `taskkill /f /im java.exe`.

3. You can ask a terminal to rerun the last command you gave it by pressing the up-arrow key. Pressing the up-arrow twice will repeat the last but one and so on.

4. The "Run Active File" trick described in Post 3.1 as a convenient way of running the contents of a script is worth re-visiting. 

### Other posts in this series ###

If you've found this post interesting and would like to find out more about Firebase you might find it worthwhile having a look at the [Index](https://ngatesystems.com/waypointsindex) to this series.