# A faster Firestore emulator, without Java

> Firenook’s Firestore emulator answers the same SDKs on port 8080: writes 40× faster, 10× less memory, no Java, and data that survives restarts.

Firenook’s Firestore emulator answers the same SDKs on the same port, enforces your Security Rules, and imports the export you have. Writes are 40× faster, it uses 10× less memory, it needs no Java, and your data is still there after a restart.

`npm install --save-dev firenook`

[Read the Firestore docs](https://firenook.dev/docs/services/firestore)

![Firenook’s console on Firestore: the collections, a page of orders and one order open for editing](https://firenook.dev/_astro/firestore-dark.BjcvnP5W_Z1kSvWK.webp)

## Measured on a real application

Two hours of a production app’s traffic against each emulator in turn, every write and listener update timed by the client.

### Memory at peak

10× less

- Official

  22.6 GiB

- Firenook

  2.11 GiB

### Write, median

40× faster

- Official

  208 ms

- Firenook

  5.1 ms

### Listener update, median

19× faster

- Official

  209 ms

- Firenook

  11 ms

### Restart with data kept

52× sooner

- Official

  30.8 s

- Firenook

  0.59 s

Firenook against firebase-tools 15.22.0 on Java 26, on one AMD Ryzen 5 3600 server with 64 GiB, with a real application with 211,205 Firestore documents and 6.7 GB in Storage. [Every result and how it was measured](https://firenook.dev/benchmarks).

## The Firestore your app already talks to

Firenook implements Firestore’s protocols and its rules engine itself, checked against the official emulator’s answers, so your SDKs and tests find what they expect.

- ### Every SDK, unchanged

  The native and Admin SDKs over gRPC, the web SDK over WebChannel, and REST, on port 8080 or the port firebase.json gives it.

- ### Realtime listeners

  Snapshots, queries and their updates as the SDKs expect them. A listener heard a write in 11 ms at the median, against 209 ms.

- ### Security Rules, reloaded as you save

  Each database’s rules file is installed a second after you save it, and a denial names the rule line that decided it. A file that does not compile leaves the rules in force and says why.

- ### Named databases

  Every database your app names, exported together. A database firebase.json does not list follows the default database’s rules. The official emulator refuses more than one.

- ### The official export format

  \--import takes the export you already have, and emulators:export writes one the official emulator can import, every database included.

- ### Data on disk

  Data lives on disk and memory caches what you read. Every acknowledged write is on disk within a second, so a crash loses at most that second.

Every production feature, edition or client version is not claimed. [What Firestore covers in 0.2.0](https://firenook.dev/compatibility#firestore), and [the release’s known issues](https://firenook.dev/compatibility#known-issues).

## Restart in half a second, with your data

Add `--keep-data` and Firestore comes back with every document in about half a second, however many there are. No export at every stop, no import at every start.

`npx firenook emulators:start --keep-data`

Official emulator

Stop: export, 50–57 s Start: import, 21.6–23.6 s

Firenook

Stop, 0.04–0.30 s Start, 0.54–0.57 s

Stopping with the data saved, then starting again with it, on one time scale. The dim blocks are the spread between the fastest and the slowest runs.

## Ordered queries from an index

orderBy, range filters and cursors walk an ordered index to the documents asked for. On a store of 2.7 million documents each answered in 3 to 6 ms, where the official emulator needed a 24 GiB heap and 8 to 28 seconds.

| Query shape, 10,918 documents             | Official | Firenook |
| ----------------------------------------- | -------- | -------- |
| orderBy an integer field, limit 20        | 96.3 ms  | 12.0 ms  |
| Range on an integer field, limit 20       | 95.5 ms  | 13.2 ms  |
| == plus orderBy on another field          | 99.3 ms  | 14.7 ms  |
| Five cursor pages of 20                   | 489 ms   | 74.8 ms  |
| orderBy a timestamp, descending, limit 20 | 109 ms   | 24.7 ms  |

Medians over gRPC, every answer identical on both. Equality filters on other fields are checked against the index before a document is read. [All 26 shapes](https://firenook.dev/benchmarks#queries).

## Questions

The rest is in [the Firestore docs](https://firenook.dev/docs/services/firestore). Something else? [Ask on GitHub](https://github.com/firenook-dev/firenook/issues).

Does my app need to change?

No. connectFirestoreEmulator(db, "127.0.0.1", 8080), FIRESTORE_EMULATOR_HOST and the rest connect as they do to the official emulator, on the port firebase.json gives Firestore.

Can I run Firestore on its own?

Yes: npx firenook emulators:start --only firestore. To keep your data between runs with --keep-data, run Auth and Storage with it; the kept data covers all three.

How much memory does it use?

In the benchmark, 2.11 GiB at most over two hours of an application’s traffic, against 22.57 GiB for the official emulator, and 0.16 GiB once ready with 211,205 documents.

Is the official emulator faster at anything?

Not in the benchmark. Of the 26 query shapes measured, Firenook was faster on 25 and tied on the last, ids only, limit 500: it reads each document whole before dropping its fields.

Can I go back to the official emulator?

Yes. Export with npx firenook emulators:export ./export while Firenook runs, and start the official emulator with --import ./export.

Does it need Java?

No. Firestore is native code in the one npm package, with an engine for macOS, Linux and Windows. Nothing else is downloaded.

## Switch your Firestore emulator

Install Firenook next to firebase-tools, and change `firebase` to `firenook` in your emulator script. Your rules, indexes, data and app stay as they are.

`npx firenook emulators:start`

[Switch in three steps](https://firenook.dev/docs/get-started/switch)
