Using SQLite Extensions with Gorm
2024 August 10

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.


Remember you can also subscribe using RSS at the top of the page!

Share this on → Mastodon Twitter LinkedIn Reddit

A selected list of related posts that you might enjoy:

*****
Written by Henry J Schmale on 2024 August 10
Hit Counter