Using BoltDB as internal database 💾
Tags: go, database, programming, opensource
Published: 2022-01-15 11:30:46

If you are looking for a small database for your fun project, I have something you might like. It is small, works as a key-value store and it's pure go.

### What is Bolt?
Bolt is a pure Go key/value store inspired by Howard Chu's LMDB project. The goal of the project is to provide a simple, fast, and reliable database for projects that don't require a full database server such as Postgres or MySQL.

Since Bolt is meant to be used as such a low-level piece of functionality, simplicity is key. The API will be small and only focus on getting values and setting values. That's it.

### How to use Bolt?
#### Installing
Bolt doesn't need any install other than `go get`. It just works as a library. So your only need is adding it to `go.mod` file.

```go get github.com/boltdb/bolt```

#### Creating database
After you can create your database like this.

```go 
	db, err := bolt.Open("my.db", 0600, &bolt.Options{})
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
```

In here you can add timeout to your database. Or you can set readonly if you don't need writing into. Just set them in `&bolt.Options{}`.

#### Writing into database
You need to create bucket first. After than you can write your key/value data into bucket. Also `tx.CreateBucketIfNotExists([]byte)` function is life saver.
```go 
	db.Update(func(tx *bolt.Tx) error {
		bucket, err := tx.CreateBucketIfNotExists([]byte("todo"))
		if err != nil {
			return fmt.Errorf("create bucket: %s", err)
		}
		return bucket.Put([]byte("task-1"), []byte("Test BoltDB"))
	})
```
If you have a bucket already you can write into it like this. Caution checking bucket is nil is important. Because if you try to use `Put` on nil bucket your program will panic.

```go
	db.Update(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte("todo"))
		if bucket == nil {
			return fmt.Errorf("get bucket: FAILED")
		}
		return bucket.Put([]byte("task-1"), []byte("Test BoltDB additions"))
	})
```

#### Querying data from database
Querying is as simple as writing. Just call your bucket and ask it for your data.
```go 
	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("todo"))
		if b == nil {
			return fmt.Errorf("get bucket: FAILED")
		}
		fmt.Println(b.Get([]byte("task-1")))
		// should return nil to complete the transaction
		return nil
	})
```
And here is simple way to iterate on all keys in one bucket.
```go 
	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("todo"))
		if b == nil {
			return fmt.Errorf("get bucket: FAILED")
		}
		// we need cursor for iteration
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			fmt.Println("Key: ", string(k), " Value: ", string(v))
		}
		// should return nil to complete the transaction
		return nil
	})
```

### Conclusion
- Bolt is a pure Go key/value store
- Bolt is native go library
- Bolt is supported everywhere which go is supported
- Creating, Writing and Querying inside Bolt is really easy.

### Simple and powerful toolkit for BoltDB
Storm is a simple and powerful toolkit for BoltDB. Basically, Storm provides indexes, a wide range of methods to store and fetch data, an advanced query system, and much more.
GitHub: https://github.com/asdine/storm


### Future Readings
- https://awmanoj.github.io/2016/08/03/using-boltdb-as-a-fast-persistent-kv-store/
- https://santoshk.dev/posts/2020/introduction-to-boltdb/