Using Golang with the Firestore Emulator
- 4 minutes read - 847 wordsThis works great but it wasn’t clearly documented for non-Firebase users. I assume it will work, as well, for any of the client libraries (not just Golang).
Assuming you have some (Golang) code (in this case using the Google Cloud Client Library) that interacts with a Firestore database. Something of the form:
package main
import (
"context"
"crypto/sha256"
"fmt"
"log"
"os"
"time"
"cloud.google.com/go/firestore"
)
func hash(s string) string {
h := sha256.New()
h.Write([]byte(s))
return fmt.Sprintf("%x", h.Sum(nil))
}
type Dog struct {
Name string `firestore:"name"`
Age int `firestore:"age"`
Human *firestore.DocumentRef `firestore:"human"`
Created time.Time `firestore:"created"`
}
func NewDog(name string, age int, human *firestore.DocumentRef) Dog {
return Dog{
Name: name,
Age: age,
Human: human,
Created: time.Now(),
}
}
func (d *Dog) ID() string {
return hash(d.Name)
}
type Human struct {
Name string `firestore:"name"`
}
func (h *Human) ID() string {
return hash(h.Name)
}
func main() {
ctx := context.Background()
project := os.Getenv("PROJECT")
client, err := firestore.NewClient(ctx, project)
if value := os.Getenv("FIRESTORE_EMULATOR_HOST"); value != "" {
log.Printf("Using Firestore Emulator: %s", value)
}
if err != nil {
log.Fatal(err)
}
defer client.Close()
me := Human{
Name: "me",
}
meDocRef := client.Collection("humans").Doc(me.ID())
if _, err := meDocRef.Set(ctx, me); err != nil {
log.Fatal(err)
}
freddie := NewDog("Freddie", 2, meDocRef)
freddieDocRef := client.Collection("dogs").Doc(freddie.ID())
if _, err := freddieDocRef.Set(ctx, freddie); err != nil {
log.Fatal(err)
}
}
Then you can interact instead with the Firestore Emulator.
First, create a firebase project. To save installing Node, NPM and a Java runtime, I chose to use Google’s Cloud Shell. In the following command, we’re configuring Cloud Shell to port-forward (from Cloud Shell to our host) ports 8080
and 4000
(to the same local ports). 8080
is the port on which the Firestore emulator will expose the database and 4000
is the port on which the UI will be exposed.
gcloud alpha cloud-shell ssh \
--ssh-flag="-L 8080:localhost:8080" \
--ssh-flag="-L 4000:localhost:4000"
Then create a test
directory and run firebase init
from it:
mkdir ./test
cd ./test
firebase init
Choose Emulators
by cursoring down and hitting the spacebar to select:
? Which Firebase CLI features do you want to set up for this folder?
◯ Database: Configure Firebase Realtime Database and deploy rules
◯ Firestore: Deploy rules and create indexes for Firestore
◯ Functions: Configure and deploy Cloud Functions
◯ Hosting: Configure and deploy Firebase Hosting sites
◯ Storage: Deploy Cloud Storage security rules
❯◉ Emulators: Set up local emulators for Firebase features
◯ Remote Config: Get, deploy, and rollback configurations for Remote Config
Hit enter, then Add Firebase to an existing Google Cloud Platform project
:
? Please select an option:
Use an existing project
Create a new project
❯ Add Firebase to an existing Google Cloud Platform project
Don't set up a default project
Choose your project from the list that the command provides.
You will then be prompted Which Firebase emulators...?
. Be careful to select Firestore Emulator
and not Database Emulator
:
=== Emulators Setup
? Which Firebase emulators do you want to set up?
◯ Authentication Emulator
◯ Functions Emulator
❯◉ Firestore Emulator
◯ Database Emulator
◯ Hosting Emulator
◯ Pub/Sub Emulator
Complete the remaining questions as shown below. I’m assuming hereafter that you left the emulator port at the default (8080
) and, that you proposed 4000
for the Emulator UI. If you want to use different ports, you’ll need to ensure that these are matched by the gcloud alpha cloud-shell ssh
command’s --ssh-flag
flags:
=== Emulators Setup
? Which port do you want to use for the firestore emulator? 8080
? Would you like to enable the Emulator UI? Yes
? Which port do you want to use for the Emulator UI? 4000
? Would you like to download the emulators now? Yes
You should receive Firebase initialization complete
and can more firebase.json
to receive:
more firebase.json
{
"emulators": {
"firestore": {
"port": 8080
},
"ui": {
"enabled": true,
"port": 4000
}
}
}
All being well, you can now type firebase emulators:start
and you should see:
firebase emulators:start
i emulators: Starting emulators: firestore
i firestore: Firestore Emulator logging to firestore-debug.log
i ui: Emulator UI logging to ui-debug.log
┌─────────────────────────────────────────────────────────────┐
│ ✔ All emulators ready! It is now safe to connect your app. │
│ i View Emulator UI at http://localhost:4000 │
└─────────────────────────────────────────────────────────────┘
┌───────────┬────────────────┬─────────────────────────────────┐
│ Emulator │ Host:Port │ View in Emulator UI │
├───────────┼────────────────┼─────────────────────────────────┤
│ Firestore │ localhost:8080 │ http://localhost:4000/firestore │
└───────────┴────────────────┴─────────────────────────────────┘
Emulator Hub running at localhost:4400
Other reserved ports: 4500
Now, from your localhost (!):
You should be able to browse the Firebase Emulator UI via http://localhost:4000
NOTE The “Firestore emulator” should have a green check mark below “Status”
You can click “Go to emulator” below “Firestore emulator” or browse http://localhost:4000/firestore
NOTE We’ve not yet added any documents, so this will be empty.
Then:
export FIRESTORE_EMULATOR_HOST="localhost:8080"
export PROJECT="[[YOUR-PROJECT]]"
go run .
NOTE The value of
${PROJECT}
must match your Google Cloud Platform Project ID; it will not work if you use an arbitrary value liketest
.
NOTE Documentation on the use of
FIRESTORE_EMULATOR_HOST
The Firestore emulator should now show collections of “dogs” and “humans”:
This is a very convenient way to prototype and test Firestore.