I’ve been building a site in Golang using Gorm as my object relational mapper because it’s a pain to map things into a struct and use the scan interface. Not really using it as a query builder, but it’s still handy. I recently wanted to use some extensions in my queries to do things like unicode normalization and similar functions. I was also sick of waiting for Cgo compilation times so I switched to a webassembly build of sqlite embedded into my go project.
Benefits to Using WebAssembly
I’m using the amazing WebAssembly SQLite build by ncruces. It’s a fantastic package that using wasm to provide architecture independence while still using the original sqlite source code without depending on automatic translation into a new language. So you benefit from the same hardcore testing that the sqlite team performs.
This does come with some downsides, if you want additional extensions you’ll have to compile them with emscripten to embed. You have to trust a pure go VFS to ensure that writes hit the database. I’ve also found that my binary is a tad bit larger compared to the Cgo version.
However the speed is very comparable with a normal sqlite build, and the portability and build time speed up really help with my test and run cycles.
1 - Get the Dependencies
go get github.com/ncruces/go-sqlite3`
go get gorm.io/gorm`
2 - Configure your SQLite Connection with Gorm
package your_db
import (
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/gormlite"
"github.com/ncruces/go-sqlite3/ext/unicode"
)
func SetupDB() (*gorm.DB, error) {
conn, err := driver.Open("test.sqlite", func(conn *sqlite3.Conn) error {
// register anything you want here. You can even register random functions
// that do one off things.
err := unicode.Register(conn)
if err != nil {
return err
}
// if there's no error go ahead and return nil
return nil
})
if err != nil {
return nil, err
}
db, err := gorm.Open(gormlite.OpenDB(conn), &gorm.Config{})
if err != nil {
return nil, err
}
// Add additional middleware to gorm. Prometheus maybe?
// Register your models and perform migrations for initial setup
return db, nil
}
You can now use the SetupDB
functions to get a gorm instance configured with your
required extensions.
I found this difficult to figure out exactly what the order of operations was I needed to get this sqlite library working and wanted to share. Hopefully I save you some time when you’re working to configure it.