Firebase Golang Binding
Eddy Reyes
https://github.com/ereyes01/firebase
Firebase Overview
● Real-time Backend-as-a-Service
● NoSQL style JSON data store
● Security rules language
○ Multi-tenancy
● User management
● REST API
○ https://mydb.firebaseio.com/path/to/data.json?
orderBy=”index”&equalTo=”tommy”
Firebase Shortcomings
● Very limited query capabilities
○ Intended for real-time applications
○ Generally only O(N) queries on one index at a time
● REST API typically lags behind JS API in
features.
● Data must be de-normalized (un-nested)
○ You’re on your own joining it all back together
Firebase Language Bindings
Except for JS, all Firebase language bindings
use the REST API
● GET / PUT / POST / PATCH
○ Read / write data
● Real-time updates via Server-Sent Events
○ Long poll protocol spec, part of HTML5
○ http://en.wikipedia.org/wiki/Server-sent_events
Firebase Go Binding
Mimics the official JS API:
type Client interface {
// path
Child(path string) Client
// read / write
Value(destination interface{}) error
Push(value interface{}, params map[string]string)
(Client, error)
Set(path string, value interface{}, params map[string]
string) (Client, error)
Update(path string, value interface{}, params map
[string]string) error
Remove(path string, params map[string]string) error
}
Example:
var w Widget
err := client.Child(“my/path”).Value(&w)
w.Name = “fred”
c, err := client.Child(“my/path”).Set(w)
c, err := client.Child(“newOne”).Push(w)
// c.Key() has name of new resource
Why Write a New Go Binding?
Options were limited:
● github.com/cosn/firebase
○ Nice approach to designing testable library
○ Incomplete, unmaintained
● github.com/JustinTulloss/firebase
○ Cleans up some of cosn’s implementation
No streaming support, tests were deficient
Firebase Go Binding
https://godoc.org/github.com/ereyes01/firebase
● Idiomatic Go implementations of:
○ Streaming (via channels)
○ Firebase server-side timestamps
■ Custom JSON encoder
○ Your Firebase data as structs (or maps if you prefer)
● Well unit-tested, testable by lib consumers
Testable Go Library
Ideal unit test:
● Tests only YOUR code being changed
● No external dependencies, no talking to the
network
○ How to mock in Go?
Interfaces!
func NewClient(root, auth string, api Api) Client
Testable Go Library
The “Api” interface handles HTTP
communication with Firebase.
● A nil api -> you talk to Firebase
(production)
● Non-nil api- test mode
○ Intercept the communication with your stub / test
implementation
Testable Go Library
type Api interface {
Call(method, path, auth string, body interface{},
params map[string]string, dest interface{}) error
Stream(path, auth string, body interface{}, params map
[string]string, stop <-chan bool) (<-chan RawEvent, error)
}
Firebase Streaming
How it works in HTTP:
● GET request, path to resource + query
● Accept: text/event-stream
● Must follow redirects
● Data:
event: patch
data: {“path”:”right/here”,“data”:{“my”: “data”}}
event: keep-alive
data: null
event: keep-alive
data: null
Firebase Streaming in Go
In Go:
● Model stream as a channel
● Channel of what type?
○ Everything unmarshals to map[string]interface{} as:
■ string / float64 / bool / map / []interface{}
○ But my “data” is my own struct type!
○ I want events via chan <MyType>
■ Type-generic channel, type is a parameter
No Generics? No Problem! (Sort of)
You can do reflection gymnastics:
○ func ParametricType(myType reflect.Type)
○ Call like this:
■ ParametricType(reflect.TypeOf(Widget{}))
■ Meanwhile, in ParametricType():
ptr := reflect.New(myType).Interface()
● Allocates a new Widget object, returns pointer
● Type-generic instantiation
No Generics? No Problem! (Sort of)
That’s a working solution, but…
Exposes reflection in an external API.
Alternative:
● interface stream + unmarshaller callback
● Longer, but perhaps cleaner interface
No Generics? No Problem! (Sort of)
Watch(unmarshaller EventUnmarshaller, stop <-chan bool)
(<-chan StreamEvent, error)
type EventUnmarshaller func(jsonText []byte) (interface{},
error)
type StreamEvent struct {
Event string
Path string
Resource interface{}
RawData string
Error error
}
Now You Can Stream Widgets!
func widgetParser(text []byte) (interface{} error) {
var w Widget
err := json.Unmarshal(text, &w)
return w, err
}
…
events, err := client.Watch(widgetParser, stopChan)
for event := range events {
widget := event.Resource.(Widget)
}
Fork Me on Github!
https://github.com/ereyes01/firebase

Firebase Golang Binding

  • 1.
    Firebase Golang Binding EddyReyes https://github.com/ereyes01/firebase
  • 2.
    Firebase Overview ● Real-timeBackend-as-a-Service ● NoSQL style JSON data store ● Security rules language ○ Multi-tenancy ● User management ● REST API ○ https://mydb.firebaseio.com/path/to/data.json? orderBy=”index”&equalTo=”tommy”
  • 3.
    Firebase Shortcomings ● Verylimited query capabilities ○ Intended for real-time applications ○ Generally only O(N) queries on one index at a time ● REST API typically lags behind JS API in features. ● Data must be de-normalized (un-nested) ○ You’re on your own joining it all back together
  • 4.
    Firebase Language Bindings Exceptfor JS, all Firebase language bindings use the REST API ● GET / PUT / POST / PATCH ○ Read / write data ● Real-time updates via Server-Sent Events ○ Long poll protocol spec, part of HTML5 ○ http://en.wikipedia.org/wiki/Server-sent_events
  • 5.
    Firebase Go Binding Mimicsthe official JS API: type Client interface { // path Child(path string) Client // read / write Value(destination interface{}) error Push(value interface{}, params map[string]string) (Client, error) Set(path string, value interface{}, params map[string] string) (Client, error) Update(path string, value interface{}, params map [string]string) error Remove(path string, params map[string]string) error } Example: var w Widget err := client.Child(“my/path”).Value(&w) w.Name = “fred” c, err := client.Child(“my/path”).Set(w) c, err := client.Child(“newOne”).Push(w) // c.Key() has name of new resource
  • 6.
    Why Write aNew Go Binding? Options were limited: ● github.com/cosn/firebase ○ Nice approach to designing testable library ○ Incomplete, unmaintained ● github.com/JustinTulloss/firebase ○ Cleans up some of cosn’s implementation No streaming support, tests were deficient
  • 7.
    Firebase Go Binding https://godoc.org/github.com/ereyes01/firebase ●Idiomatic Go implementations of: ○ Streaming (via channels) ○ Firebase server-side timestamps ■ Custom JSON encoder ○ Your Firebase data as structs (or maps if you prefer) ● Well unit-tested, testable by lib consumers
  • 8.
    Testable Go Library Idealunit test: ● Tests only YOUR code being changed ● No external dependencies, no talking to the network ○ How to mock in Go? Interfaces! func NewClient(root, auth string, api Api) Client
  • 9.
    Testable Go Library The“Api” interface handles HTTP communication with Firebase. ● A nil api -> you talk to Firebase (production) ● Non-nil api- test mode ○ Intercept the communication with your stub / test implementation
  • 10.
    Testable Go Library typeApi interface { Call(method, path, auth string, body interface{}, params map[string]string, dest interface{}) error Stream(path, auth string, body interface{}, params map [string]string, stop <-chan bool) (<-chan RawEvent, error) }
  • 11.
    Firebase Streaming How itworks in HTTP: ● GET request, path to resource + query ● Accept: text/event-stream ● Must follow redirects ● Data: event: patch data: {“path”:”right/here”,“data”:{“my”: “data”}} event: keep-alive data: null event: keep-alive data: null
  • 12.
    Firebase Streaming inGo In Go: ● Model stream as a channel ● Channel of what type? ○ Everything unmarshals to map[string]interface{} as: ■ string / float64 / bool / map / []interface{} ○ But my “data” is my own struct type! ○ I want events via chan <MyType> ■ Type-generic channel, type is a parameter
  • 13.
    No Generics? NoProblem! (Sort of) You can do reflection gymnastics: ○ func ParametricType(myType reflect.Type) ○ Call like this: ■ ParametricType(reflect.TypeOf(Widget{})) ■ Meanwhile, in ParametricType(): ptr := reflect.New(myType).Interface() ● Allocates a new Widget object, returns pointer ● Type-generic instantiation
  • 14.
    No Generics? NoProblem! (Sort of) That’s a working solution, but… Exposes reflection in an external API. Alternative: ● interface stream + unmarshaller callback ● Longer, but perhaps cleaner interface
  • 15.
    No Generics? NoProblem! (Sort of) Watch(unmarshaller EventUnmarshaller, stop <-chan bool) (<-chan StreamEvent, error) type EventUnmarshaller func(jsonText []byte) (interface{}, error) type StreamEvent struct { Event string Path string Resource interface{} RawData string Error error }
  • 16.
    Now You CanStream Widgets! func widgetParser(text []byte) (interface{} error) { var w Widget err := json.Unmarshal(text, &w) return w, err } … events, err := client.Watch(widgetParser, stopChan) for event := range events { widget := event.Resource.(Widget) }
  • 17.
    Fork Me onGithub! https://github.com/ereyes01/firebase