BoltDB API
Dec 29, 2015Couple of days ago I wrote BoltApi, a REST API on top of BoltDB which is a library based key-value store. Yes, its a library which means you use it by calling the library APIs on your app as opposed to talking to a separate process.
You can run it like:
boltapi -dbpath=./app.db -port=8080
It uses go-json-rest to expose REST endpoints, which means its easy to offer one of the middlewares for the BoltApi. So if I want to add gzip compression later on, or basic auth then we just include those middlewares or better yet offer command line flag like:
boltapi -dbpath=./app.db -port=8080 -with-basic-auth -with-gzip
Making the API ready for production out of the box. The REST API itself is simple, it exposes the following endpoints:
Buckets endpoint
/api/v1/buckets
GET - List buckets
POST - Add bucket
Bucket endpoint (singular)
/api/v1/buckets/:name
GET - List bucket items
POST - Add item on the bucket
DELETE - Delete bucket
Bucket item endpoint
/api/v1/buckets/:name/:key
GET - Retrieve item
PUT - Update item
DELETE - Delete item
Building the REST API was straight forward, BoltDB offers APIs for read-only and read/write transactions. Here’s an excerpt from BoltApi for retrieving a bucket:
func (restapi *RestApi) GetBucket(w rest.ResponseWriter, r *rest.Request) {
bucketName := r.PathParam("name")
items := []*BucketItem{}
if err := restapi.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
if bucket == nil {
return ErrBucketMissing
}
return bucket.ForEach(func(k, v []byte) error {
bucketItem := &BucketItem{Key: string(k)}
bucketItem.DecodeValue(v)
items = append(items, bucketItem)
return nil
})
}); err != nil {
log.Println(ApiError{ErrBucketGet, err})
switch err {
case ErrBucketGet:
rest.Error(w, ErrBucketGet.Error(), http.StatusInternalServerError)
case ErrBucketMissing:
rest.Error(w, ErrBucketMissing.Error(), http.StatusInternalServerError)
}
return
}
w.WriteJson(items)
}
The db.View
is the read-only transaction and there’s also db.Update
which supports read and write. Notice how compact the code is, the param to both transactions is just a function that accepts bolt.Tx
- BoltDB’s transaction object and everything you do inside that function is enclosed in that transaction.
So next time you have a Go project that needs key-value storage, consider BoltDB or BoltApi if you want a REST API on top of it.