Firestore
gRPC · port 8088
Configuration
Set the emulator host environment variable:
$ export FIRESTORE_EMULATOR_HOST=localhost:8088 # or use: eval $(localgcp env)
The Go, Python, Java, and Node.js Firestore client libraries all respect this env var.
Go SDK example
Create a document, read it back, and run a query:
package main import ( "context" "fmt" "log" "cloud.google.com/go/firestore" ) func main() { ctx := context.Background() // FIRESTORE_EMULATOR_HOST=localhost:8088 must be set client, err := firestore.NewClient(ctx, "my-project") if err != nil { log.Fatal(err) } defer client.Close() // Create a document _, err = client.Collection("users").Doc("alice").Set(ctx, map[string]interface{}{ "name": "Alice", "age": 30, "tags": []string{"admin", "dev"}, }) if err != nil { log.Fatal(err) } // Read it back doc, err := client.Collection("users").Doc("alice").Get(ctx) if err != nil { log.Fatal(err) } fmt.Printf("Name: %s\n", doc.Data()["name"]) // Query: find users older than 25 iter := client.Collection("users"). Where("age", ">", 25). OrderBy("age", firestore.Asc). Documents(ctx) defer iter.Stop() for { doc, err := iter.Next() if err != nil { break } fmt.Printf(" %s (age %v)\n", doc.Data()["name"], doc.Data()["age"]) } }
Real-time listeners
localgcp supports the Listen streaming RPC, which powers onSnapshot in the client libraries. Changes to documents are pushed to listeners in real time:
// Listen for changes to a document snapIter := client.Collection("users").Doc("alice").Snapshots(ctx) defer snapIter.Stop() for { snap, err := snapIter.Next() if err != nil { break } fmt.Printf("Document changed: %v\n", snap.Data()) } // Listen for changes to a collection query queryIter := client.Collection("users"). Where("age", ">", 25). Snapshots(ctx) defer queryIter.Stop() for { snap, err := queryIter.Next() if err != nil { break } for _, change := range snap.Changes { fmt.Printf(" %s: %v\n", change.Doc.Ref.ID, change.Doc.Data()) } }
Query operators
Supported query operators in the Where clause:
==-- equality<,<=,>,>=-- range comparisonsin-- value in a listnot-in-- value not in a listarray-contains-- array field contains a valuearray-contains-any-- array field contains any of the values
// IN query client.Collection("users").Where("name", "in", []string{"Alice", "Bob"}) // ARRAY_CONTAINS client.Collection("users").Where("tags", "array-contains", "admin") // ARRAY_CONTAINS_ANY client.Collection("users").Where("tags", "array-contains-any", []string{"admin", "ops"}) // NOT_IN client.Collection("users").Where("name", "not-in", []string{"Charlie"})
Transactions
localgcp supports Firestore transactions with begin, commit, and rollback:
err = client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error { doc, err := tx.Get(client.Collection("accounts").Doc("alice")) if err != nil { return err } balance := doc.Data()["balance"].(int64) return tx.Set(client.Collection("accounts").Doc("alice"), map[string]interface{}{ "balance": balance + 100, }) })
Not yet supported
- Composite indexes
- Collection group queries
- Resume tokens for listeners