Cloud Firestore client helper library
Cloud Firestore is a Serverless document database. It comes with with multi-regional replication, powerful query engine, and seamless integration into the broader Google Cloud. I found myself use it frequently over last few years. While Firestore exposes both HTTP and RPC APIs to which you can make direct calls, most people rely on one of the client libraries, and the Go library for Firestore is certainly well documented.
Having used Firestore on a few projects though, I did find it wee bit verbose and repetitive in some places. Oven is a wrapper the the standard Firestore client library. It hides some of the complexity (e.g. iterator over resulting documents), and shortens a few of the more verbose, but common, use-cases.
- Easy Save, Update, Get, Delete
- Structured criteria Query
- Extends Firestore client
go get github.com/mchmarny/oven
The examples folder includes some of the most common use-cases with two fully functional code:
- Basic CRUD - Save, Get, Update, Delete operations
- Structured Query - With automatic result mapping to a slice
b := Book{
ID: "id-123",
Title: "The Hitchhiker's Guide to the Galaxy",
Author: "Douglas Adams",
}
if err := oven.Save(ctx, client, "books", b.ID, &b); err != nil {
handleErr(err)
}
b, err := oven.Get[Book](ctx, client, "books", "id-123")
if err != nil {
handleErr(err)
}
Using
Title
field withfirestore:"title"
tag
m := map[string]interface{}{
"title": "Some new title",
}
if err := oven.Update(ctx, client, "books", "id-123", m); err != nil {
handleErr(err)
}
if err := oven.Delete(ctx, client, "books", "id-123"); err != nil {
handleErr(err)
}
q := &oven.Criteria{
Collection: book.CollectionName,
Criterions: []*oven.Criterion{
{
Path: "author", // `firestore:"author"`
Operation: oven.OperationTypeEqual,
Value: bookAuthor,
},
},
OrderBy: "published", // `firestore:"published"`
Desc: true,
Limit: bookCollectionSize,
}
list, err := oven.Query(ctx, client, q)
if err != nil {
handleErr(err)
}
In case you already have the Firestore iterator and want to just avoid the verbose for
loop of spooling the documents into a list, oven
provides the ToStructs
method
// given it as *firestore.DocumentIterator
it := col.Documents(ctx)
// this returns a slice of books ([]*Book)
list, err := oven.ToStructs[Book](it)
There is also support for a handler in case you need to reason over individual item. When handler returns an error, the entire ToStructsWithHandler
method will exit with that error.
Only items for which the handler returns
true
will be appended to the result slice. This allows for filtering the results when such filters are not possible in the Firestore itself.
it := col.Documents(ctx)
return store.ToStructsWithHandler(it, func(item *Person) (bool, error) {
item.Age = time.Since(item.DOB)
return true, nil
})
This is my personal project and it does not represent my employer. While I do my best to ensure that everything works, I take no responsibility for issues caused by this code.