> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tablepro.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Firebase

> Connect to Cloud Firestore and the Firebase Realtime Database, browse collections and nodes, and edit documents

export const name_0 = "Cloud Firestore"

One plugin serves two connection types. In **Cloud Firestore** a collection is a table and a document is a row. In **Firebase Realtime Database** the children of a node are the rows. Neither has SQL: the editor takes REST request lines such as `GET users/alice`.

The {name_0} driver is not in the app. Picking {name_0} in the **Choose a Database** sheet offers the
download before the form opens, and opening a saved {name_0} connection installs it without asking.
See [Plugins](/features/plugins).

## Quick setup

Click **New Connection…**, select **Cloud Firestore** or **Firebase Realtime Database**, pick an **Auth Method**, then click **Save & Connect**.

With a service account key, leave **Project ID** empty: the key names the project. Leave **Database URL** empty as well, and the Realtime Database connection opens the project's default instance.

## Connection settings

| Field | Shown for | Notes |
| - | - | - |
| **Auth Method** | Both | **Service Account Key** (the default), **Application Default Credentials**, or **Emulator (no credentials)** |
| **Service Account Key** | Service Account Key | A path to the JSON key file, or the key JSON itself. Stored in the Keychain |
| **Project ID** | Both | Empty takes the project from the credentials |
| **Database** | Cloud Firestore | Empty means `(default)`. The database switcher lists the project's other Firestore Native databases |
| **Database URL** | Realtime Database | `https://<name>.firebaseio.com` or `https://<name>.<region>.firebasedatabase.app`. Empty finds the project's default instance, and the database switcher lists the others |
| **Emulator Host** | Emulator | `host:port` of the emulator |

Both are managed services, so any version works, and there is no host, port or SSH tunnel to set.

## Connection URL

There is no URL scheme for Firebase. Create the connection from the form.

## Authentication

Pick **Service Account Key** and generate a key in the Firebase console under Project settings, Service accounts. On a Mac where `gcloud` is already signed in, **Application Default Credentials** needs no file.

| Auth Method | What is sent |
| - | - |
| **Service Account Key** | An OAuth token signed with the key |
| **Application Default Credentials** | The token from `gcloud auth application-default login`, or from the key file `GOOGLE_APPLICATION_CREDENTIALS` names. An empty **Project ID** falls back to the project in that file, then to `GOOGLE_CLOUD_PROJECT` |
| **Emulator (no credentials)** | The token `owner`, which the Firebase emulators treat as an administrator |

Access comes from the account's IAM roles, not from security rules. Cloud Datastore Viewer reads Firestore and Cloud Datastore User also writes it. Firebase Realtime Database Viewer reads the Realtime Database and Firebase Realtime Database Admin also writes it.

## Cloud Firestore

### Browsing collections

The sidebar lists the root collections, then their subcollections as collection groups such as `*/orders`, which reads every `orders` collection at any depth.

Every grid has `__name__` first: the document ID, or the full path in a collection group. The other columns are the fields the documents on the page hold, in alphabetical order. A field a document lacks shows as **No Field**, not NULL. Maps and arrays show as JSON, a geopoint as `latitude, longitude`, and a reference as the path it points to.

### Filtering and sorting

Filters and sorts run as one structured query. A filter value takes the type the field holds, so `age = 30` matches the integer 30, and `zip = 02134` matches the string when `zip` holds strings.

| Operators | What Firestore runs |
| - | - |
| equals, not equals | `EQUAL`, `NOT_EQUAL` |
| greater than, greater or equal, less than, less or equal | Range filters |
| in list, not in list | `IN`, `NOT_IN`, with 1 to 30 values |
| between | Two bounds, both inclusive |
| starts with | A range from the prefix to the prefix followed by `U+F8FF` |
| contains | `ARRAY_CONTAINS`, on an array field only |
| is NULL, is not NULL | `IS_NULL`, `IS_NOT_NULL` |

**contains** and **not contains** on a string field, **ends with**, the empty checks and **REGEX** are refused with the reason, since Firestore has no text search. Matching is always case-sensitive. **Match any** joins the rows with OR.

A raw filter row takes one `Filter` object from the Firestore REST API, which reaches nested fields: `{"fieldFilter": {"field": {"fieldPath": "address.city"}, "op": "EQUAL", "value": {"stringValue": "Milan"}}}`.

A sort on a field leaves out the documents that do not have it, and the row count leaves them out too.

### Editing

Each saved change is one request:

| Change | Request |
| - | - |
| New row | `POST users?documentId=alice`. With `__name__` empty, Firestore generates the ID. In a collection group, `__name__` is the full path, such as `users/alice/orders/o1` |
| Edited cells | `PATCH users/alice?updateMask=age`, with only the fields you changed. It fails on a document deleted in the meantime instead of recreating it |
| Removed field | The field in `updateMask` and not in the body |
| Deleted row | `DELETE users/alice` |

An edited cell keeps the type the field had: a string stays a string even when the new text is a number. A cell that held null, or no field, takes the type the column holds on other rows. Timestamps take RFC 3339 text such as `2026-10-12T08:00:00Z`, geopoints `45.46, 9.19`, bytes base64, and references a document path.

**Edit Document** opens the whole document as JSON. Types JSON lacks are written as one-key objects:

```json theme={null}
{
  "__name__": "users/alice",
  "age": 31,
  "since": {"$timestamp": "2026-10-12T08:00:00Z"},
  "owner": {"$ref": "teams/core"},
  "home": {"$geo": {"latitude": 45.46, "longitude": 9.19}},
  "avatar": {"$bytes": "aGk="},
  "ratio": {"$double": 2}
}
```

`$double` also takes `"NaN"`, `"Infinity"` and `"-Infinity"`. A map whose only key is one of these tags goes inside `$map`. **Insert Document** takes the same JSON, with `__name__` as the ID or left out for a generated one.

Saving reads the document again and stops if it changed after you opened it, so another writer's change is never overwritten.

### Request editor

| Request | What it does |
| - | - |
| `GET users` | The first 100 documents of a collection. `?pageSize=` sets another number |
| `GET */orders` | Every `orders` collection at any depth |
| `GET users/alice` | One document |
| `GET /` | The root collection IDs. `GET users/alice?collections` lists a document's subcollections |
| `QUERY {"structuredQuery": {...}}` | A structured query from the REST reference, with REST values such as `{"integerValue": "30"}`. `QUERY users/alice {...}` runs it under a document |
| `POST users {"name": "Bob"}` | Creates a document with a generated ID. `POST users?documentId=bob` names it |
| `PATCH users/alice?updateMask=age {"age": 31}` | Changes the named fields. Without `updateMask` the body replaces the document. Never creates one |
| `DELETE users/alice` | Deletes a document |

`POST` and `PATCH` bodies use the **Edit Document** JSON, on the same line or the lines under it. There is no `PUT`. Write a space or any of `% ? # & = /` inside an ID as its percent escape, such as `GET users/John%20Doe`. `DELETE` raises the dangerous query warning even on **Silent**. Other writes follow the connection's [Safe Mode](/features/safe-mode) level.

## Realtime Database

### Browsing nodes

The sidebar lists `/`, the root, and the root's children when there are 500 or fewer. Open `/` for a root with more.

Every grid has `$key` first. Children that are objects add their fields as columns, and children that are plain values go in `$value`. Nested objects show as JSON. A node holding one plain value, such as `/maintenance`, shows one row with that path as its `$key`, and editing it writes the node itself.

### Filtering and sorting

Filter on one column, `$key`, `$value` or a child field, and sort only by that column. The filter bar offers **Match all** only.

| Operators | What the Realtime Database runs |
| - | - |
| equals | `equalTo` |
| greater than, greater or equal | `startAfter`, `startAt` |
| less than, less or equal | `endBefore`, `endAt` |
| between | `startAt` and `endAt` |
| starts with | `startAt` and `endAt` with `U+F8FF` |
| is NULL | `equalTo=null` |

Other operators are refused with the reason. Ordering by a child field needs `.indexOn` for it in the database rules.

A raw filter row takes a query string as written, such as `orderBy="age"&startAt=18`. When it names no limit, `limitToFirst` is added for the page, and `orderBy="$key"` too when it names no order.

Each page asks for every child up to the end of that page and drops the ones before it. Counting lists every key, so a grid shows no total until you click **Count Exactly**, and a filtered grid shows none at all.

### Editing

| Change | Request |
| - | - |
| New row | `PUT` under the `$key` you typed, replacing any child already there, or `POST` for a generated key |
| Edited cells | `PATCH` on the child with the changed fields |
| Edited `$value` | `PUT` on the child |
| Removed field | The field set to `null` in the `PATCH` |
| Deleted row | `DELETE` on the child |

A key cannot be empty or contain `.`, `#`, `$`, `[`, `]` or `/`.

**Edit Document** opens a child as `{"$key": "<path>", "$value": <JSON>}`. Saving reads the node again and writes only if it still matches what you opened. **Insert Document** takes the same shape, or bare JSON for a child under a generated key.

### Request editor

```http theme={null}
GET /chats?orderBy="$key"&limitToFirst=20
```

`GET`, `PUT`, `PATCH`, `POST` and `DELETE` take a path and, for writes, a JSON body on the same line or the lines under it. Query parameters apply to `GET` only, and a `PATCH` body is an object of the children to change. `PUT /` and `DELETE /` are refused: write or delete the root's children one at a time.

A `DELETE`, a `PUT` that leaves the node empty and a `PATCH` that sets a child to `null` raise the dangerous query warning even on **Silent**. Other writes follow the connection's [Safe Mode](/features/safe-mode) level.

## Emulators

Pick **Emulator (no credentials)**, enter the emulators' project as **Project ID**, and set **Emulator Host** to the Firestore or Database emulator. Unless `firebase.json` moves them, they listen on `127.0.0.1:8080` and `127.0.0.1:9000`.

The Realtime Database connection opens the `<project>-default-rtdb` namespace. For another, set **Database URL** to `http://127.0.0.1:9000/?ns=<name>`.

## SSL/TLS

Requests to Google are always HTTPS, and there is no **SSL Mode**. The emulators are plain HTTP.

## Limitations

* No real-time listeners. A grid shows what was read; refresh it to see later changes.
* Firestore pages with `offset`, which Firestore bills as one read per skipped document. Filter to reach late rows rather than paging to them. Export reads with a cursor and skips nothing.
* Realtime Database pages download every child before the page, so late pages of a large node are slow. Filter on `$key` to start further in.
* Export fixes its columns from the first page. Fields that appear only later go into an `__extra__` column as JSON.
* Import is not available. Write documents from the request editor.
* The Structure tab lists the fields of a 50-document sample and is read-only. A field few documents hold can be missing from it.
* Datastore-mode databases are not listed. Use a Firestore Native database.
* Collection groups come from the first two documents of up to 30 root collections, one level down, and the list is kept for 10 minutes. Read any other path from the request editor, such as `GET users/alice/orders`.
* Not available on iPhone or iPad.

## Troubleshooting

### Permission denied: …

The account lacks the IAM role for the operation named after the colon. Grant one of the roles under [Authentication](#authentication), or connect with an account that has it.

### Enter a Project ID, or use a service account key that names one.

A Firestore connection found no project: the Application Default Credentials file names none and `GOOGLE_CLOUD_PROJECT` is not set. Fill in **Project ID**.

### Enter a Database URL or a Project ID.

A Realtime Database connection needs one of the two to find the instance. Fill in either.

### "…" is not a Realtime Database URL.

**Database URL** is not a Firebase address. Copy it from the Realtime Database page of the Firebase console, or clear it to use the default instance.

### The query requires an index. …

Firestore needs a composite index for this combination of filters and sort. Open the link, create the index, and run the filter again once the index has built.

### Index not defined, add ".indexOn": "…", for path "…", to the rules

The Realtime Database orders by a child field only with an index. Add the rule the message names in the Firebase console, under Realtime Database, Rules.

### The document changed after it was opened. Reopen it and edit again.

Another writer saved the document between **Edit Document** opening it and your save. Open it again and redo the change.

### The node changed after it was opened. Reopen it and edit again.

The Realtime Database node was written after **Edit Document** read it. Open it again and redo the change.

## Related

* [Filtering](/features/filtering)
* [Safe Mode](/features/safe-mode)
* [Import & Export](/features/import-export)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.