| title | Firebase |
|---|---|
| description | Connect to Cloud Firestore and the Firebase Realtime Database, browse collections and nodes, and edit documents |
import RegistryPlugin from "/snippets/registry-plugin.mdx";
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.
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.
| 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.
There is no URL scheme for Firebase. Create the connection from the form.
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.
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.
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.
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:
{
"__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 | 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 level.
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.
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.
| 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.
GET /chats?orderBy="$key"&limitToFirst=20GET, 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 level.
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>.
Requests to Google are always HTTPS, and there is no SSL Mode. The emulators are plain HTTP.
- 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
$keyto 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.
The account lacks the IAM role for the operation named after the colon. Grant one of the roles under Authentication, or connect with an account that has it.
A Firestore connection found no project: the Application Default Credentials file names none and GOOGLE_CLOUD_PROJECT is not set. Fill in Project ID.
A Realtime Database connection needs one of the two to find the instance. Fill in either.
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.
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.
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.
Another writer saved the document between Edit Document opening it and your save. Open it again and redo the change.
The Realtime Database node was written after Edit Document read it. Open it again and redo the change.