Skip to content
GitHub
Get started →

Firebase adapter

The Firebase adapter queries Cloud Firestore via the Firebase Admin SDK. Use this when your data lives in Firestore documents.

Config shape

Inline service account:

{
"type": "firebase",
"config": {
"projectId": "your-gcp-project",
"serviceAccount": {
"projectId": "your-gcp-project",
"clientEmail": "spelo-readonly@your-gcp-project.iam.gserviceaccount.com",
"privateKey": "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----\n"
}
},
"collections": {
"properties": {
"source": "properties",
"searchable_fields": ["address", "city"],
"filterable_fields": ["beds", "city", "price"],
"display_fields": ["id", "address", "city", "beds", "price"]
}
}
}

JSON string form (easier to paste):

{
"type": "firebase",
"config": {
"serviceAccountJson": "{\"type\":\"service_account\",\"project_id\":\"...\",\"client_email\":\"...\",\"private_key\":\"...\"}"
}
}

Application Default Credentials (if running in GCP, optional):

{
"type": "firebase",
"config": {
"projectId": "your-gcp-project",
"useApplicationDefault": true
}
}

Setup

  1. Create a service account with read-only Firestore access

    GCP Console → IAM & AdminService AccountsCreate Service Account.

    Name: spelo-readonly. Grant the Cloud Datastore Viewer role (which also covers Firestore read). Do not grant Editor / Owner.

  2. Create a key

    Actions → Manage KeysAdd KeyJSON. A file downloads. Keep it secret.

  3. Paste in the dashboard

    Dashboard → DataFirebase → paste the entire JSON into the Service Account field → Test connection.

  4. Map collections

    The source is the Firestore collection path. Sub-collections use /, e.g. users/abc/orders (but the adapter currently queries top-level collections — use a webhook if you need sub-collection queries).

Operator translation

SearchParams operatorFirestore where()
eqwhere(field, '==', value)
neqwhere(field, '!=', value)
gt, gte, lt, ltestandard Firestore range ops
contains (array field)where(field, 'array-contains', value)
contains (string field)in-memory filter after fetch (Firestore has no substring op)
inwhere(field, 'in', values) — max 30

Free-text query — Firestore does not support substring search. The adapter fetches up to 50 documents matching the structured filters, then filters in-memory via the shared matchesQuery helper.

Indexes

Firestore requires composite indexes for queries with multiple filters. Run your query once; Firestore’s error message gives you a clickable link to auto-create the index. Create these indexes ahead of time for your expected filter combos.

Security notes

  • Read-only via IAM. The service account must have only roles/datastore.viewer. No write roles.
  • Collection path segments validated against ^[A-Za-z0-9_]+$ — nothing with dots (Firestore field paths) or slashes-out-of-place.
  • Adapter code only ever calls .get() and .where() / .orderBy() / .limit() — no set(), update(), delete(), runTransaction.

Troubleshooting

  • PERMISSION_DENIED: Missing or insufficient permissions. → the service account doesn’t have datastore.viewer. Grant it.
  • FAILED_PRECONDITION: The query requires an index. → follow the link in the error message to create the composite index.
  • INVALID_ARGUMENT: Inequality filter property and first sort order must be the same → your filter combination isn’t supported by Firestore. Simplify or switch to a webhook.
  • Could not load the default credentials → you set useApplicationDefault: true but the environment has no ADC. Fall back to inline or JSON service account.

More: Database connection errors.