How to Use Turso with Go: Complete Guide to the tursogo Database Driver
The tursogo driver implements the standard database/sql interface, allowing you to connect to Turso databases in Go by importing turso.tech/database/tursogo and using sql.Open("turso", dsn) for local files or turso.NewTursoSyncDb() for cloud-synced instances.
The tursogo package in the tursodatabase/turso repository provides native Go support for Turso's SQLite-compatible databases. It implements the standard database/sql driver interface, meaning any code that works with database/sql works with Turso without modification, while also offering advanced synchronization features for edge computing scenarios.
Driver Architecture and Core Components
The tursogo driver bridges Go's standard database abstraction with Turso's native C API, supporting both standalone local databases and synchronized edge replicas.
Driver Registration and Initialization
In bindings/go/driver_db.go lines 70-73, the driver registers itself with Go's database/sql package via sql.Register("turso", &tursoDbDriver{}). This registration happens automatically when you import the package.
The InitLibrary function in bindings/go/bindings.go lines 10-21 handles one-time loading of the platform-specific native library, ensuring thread-safe initialization exactly once per process.
Connection Lifecycle
When you call sql.Open("turso", dsn), the driver's Open method (implemented in driver_db.go lines 90-129) performs several critical operations:
- Parses the DSN using
parseDSN(lines 219-255 indriver_db.go) - Invokes
InitLibraryto load native bindings - Creates the underlying database via
turso_database_new - Establishes a connection via
turso_database_connect - Applies the busy-timeout if specified in the DSN
The tursoDbConnection struct (lines 131-200 in driver_db.go) implements driver.Conn, driver.ExecerContext, and driver.QueryerContext, translating standard SQL calls to Turso's low-level C API through wrappers like turso_statement_bind_positional_* and turso_statement_step.
Synced Database Architecture
For edge-sync scenarios, the driver provides TursoSyncDb in bindings/go/driver_sync.go. This high-level wrapper manages:
- Push/pull replication with Turso Cloud
- Checkpointing of the write-ahead log
- Automatic IO processing via an
extraIohook
The tursoSyncConnector.Connect method (lines 99-106 in driver_sync.go) attaches the processOneIo callback, allowing the SQL executor to interleave network and file IO while stepping statements.
Basic Usage: Local SQLite Databases
For standalone local databases without cloud synchronization, use the standard database/sql workflow:
package main
import (
"database/sql"
"log"
_ "turso.tech/database/tursogo" // registers the "turso" driver
)
func main() {
// DSN: path to the SQLite file + optional query params (e.g. _busy_timeout=5000)
db, err := sql.Open("turso", "example.db?_busy_timeout=5000")
if err != nil {
log.Fatalf("open: %v", err)
}
defer db.Close()
// Create a table
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
)
`)
if err != nil {
log.Fatalf("create: %v", err)
}
// Insert a row
res, err := db.Exec(`INSERT INTO items (name) VALUES (?)`, "hello")
if err != nil {
log.Fatalf("insert: %v", err)
}
id, _ := res.LastInsertId()
log.Printf("inserted row id=%d", id)
// Query back
var name string
row := db.QueryRow(`SELECT name FROM items WHERE id = ?`, id)
if err := row.Scan(&name); err != nil {
log.Fatalf("query: %v", err)
}
log.Printf("retrieved name=%s", name)
}
The driver handles all statement preparation and execution through tursoDbStatement and tursoDbConnection (see driver_db.go lines 131-200), translating your Go SQL calls to native Turso operations.
Advanced Usage: Synced Databases with Turso Cloud
For applications requiring synchronization between local storage and Turso Cloud, use NewTursoSyncDb:
package main
import (
"context"
"database/sql"
"log"
"os"
"os/signal"
"syscall"
"time"
turso "turso.tech/database/tursogo"
)
func main() {
// Required env vars – see the example in the repo (examples/go/sync/sync.go)
remoteURL := os.Getenv("TURSO_DATABASE_URL")
authToken := os.Getenv("TURSO_AUTH_TOKEN")
if remoteURL == "" || authToken == "" {
log.Fatal("TURSO_DATABASE_URL and TURSO_AUTH_TOKEN must be set")
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
// Build a synced DB wrapper
syncDB, err := turso.NewTursoSyncDb(ctx, turso.TursoSyncDbConfig{
Path: "local.db", // local file
RemoteUrl: remoteURL, // libsql://… or https://…
AuthToken: authToken,
})
if err != nil {
log.Fatalf("NewTursoSyncDb: %v", err)
}
defer syncDB.Checkpoint(ctx) // optional final checkpoint
// Obtain a *sql.DB that uses the sync connector
db, err := syncDB.Connect(ctx)
if err != nil {
log.Fatalf("Connect: %v", err)
}
defer db.Close()
// Normal SQL usage …
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS events (id INTEGER PRIMARY KEY, msg TEXT, ts INTEGER)`)
if err != nil {
log.Fatalf("create table: %v", err)
}
_, err = db.Exec(`INSERT INTO events (msg, ts) VALUES (?, ?)`, "first", time.Now().Unix())
if err != nil {
log.Fatalf("insert: %v", err)
}
// Periodically push local changes and pull remote ones
go func() {
tick := time.NewTicker(60 * time.Second)
for {
select {
case <-ctx.Done():
return
case <-tick.C:
if err := syncDB.Push(ctx); err != nil {
log.Printf("[push] error: %v", err)
}
changed, err := syncDB.Pull(ctx)
if err != nil {
log.Printf("[pull] error: %v", err)
} else if changed {
log.Println("[pull] applied remote changes")
}
}
}
}()
<-ctx.Done()
log.Println("shutting down")
}
The NewTursoSyncDb function (lines 14-44 in driver_sync.go) creates the underlying sync engine via turso_sync_database_new, parses the DSN using parseSyncDSN (lines 111-132), and sets up the IO processing queue. The Connect method returns a standard *sql.DB backed by tursoSyncConnector, which handles driveOpUntilDone and processIoQueue internally.
Configuration and DSN Options
DSN Parameters
The driver parses Data Source Names in driver_db.go lines 219-255 via parseDSN. Key parameters include:
_busy_timeout: Duration in milliseconds the database waits before returning aSQLITE_BUSYerror- File path: The local SQLite database file location
For synced databases, parseSyncDSN (lines 111-132 in driver_sync.go) handles additional authentication parameters.
Using the Connector API
For explicit configuration, use NewConnector to create a TursoConnector (lines 44-74 in driver_db.go):
import (
"context"
"database/sql"
_ "turso.tech/database/tursogo"
"turso.tech/database/tursogo"
)
func main() {
// Use the connector API to set a 10‑second busy timeout
connector, _ := turso.NewConnector(
"mydb.db?_busy_timeout=10000",
turso.WithBusyTimeout(10000), // same value, overrides DSN if present
)
db := sql.OpenDB(connector) // regular *sql.DB
defer db.Close()
// … use db as usual
}
The connector stores the DSN and any ConnectorOptions, overriding busy-timeout values in the parsed config before opening the native connection (see driver_db.go lines 77-91).
Core Source Files Reference
Understanding the implementation helps debug complex scenarios:
bindings/go/driver_db.go: Coredatabase/sqldriver implementation, DSN parsing, connection and statement handling (lines 70-200)bindings/go/driver_sync.go: High-level synced-database wrapper (TursoSyncDb),tursoSyncConnectorimplementation, push/pull/checkpoint logic, and IO processing (lines 14-132)bindings/go/bindings.go: One-time loading of the platform-specific native library viaInitLibrary(lines 10-21)examples/go/sync/sync.go: Working reference implementation demonstrating sync operations
Summary
- Import once: Use
_ "turso.tech/database/tursogo"to register the"turso"driver name withdatabase/sql - Local mode: Call
sql.Open("turso", "file.db?_busy_timeout=5000")for standalone databases; theOpenfunction indriver_db.gohandles native library initialization and connection setup - Sync mode: Use
turso.NewTursoSyncDb()for edge-computing scenarios requiring cloud synchronization; it manages push/pull operations and checkpointing viadriver_sync.go - Standard interface: Both modes return
*sql.DBcompatible with standarddatabase/sqlpatterns, including connection pooling, prepared statements, and transaction handling - IO interleaving: Synced connections use the
extraIohook (processOneIo) to perform network operations without blocking SQL execution
Frequently Asked Questions
What is the difference between using sql.Open and NewTursoSyncDb?
sql.Open("turso", dsn) creates a standard connection to a local SQLite database file managed by Turso's engine, suitable for single-node applications. NewTursoSyncDb creates a high-level wrapper (TursoSyncDb) that manages bidirectional synchronization between a local database file and Turso Cloud, handling replication via Push, Pull, and Checkpoint methods defined in driver_sync.go.
How does the driver handle network IO during SQL execution?
The synced driver implements an extraIo callback mechanism set in tursoSyncConnector.Connect (lines 99-106 in driver_sync.go). During statement stepping, the driver yields control to processOneIo, which processes network operations (push/pull) without blocking the main SQL execution thread, enabling asynchronous replication while maintaining query responsiveness.
Can I use standard database/sql features like connection pooling?
Yes. The tursogo driver implements the complete database/sql driver interface including driver.Conn, driver.Stmt, and driver.Tx. Connections returned via sql.Open or tursoSyncDb.Connect support standard connection pooling, prepared statements via PrepareContext (lines 131-200 in driver_db.go), and full transaction support via BeginTx.
How do I configure the busy timeout for concurrent access?
Set the _busy_timeout parameter in the DSN (e.g., "my.db?_busy_timeout=5000"), which is parsed by parseDSN in driver_db.go lines 219-255. Alternatively, use the WithBusyTimeout() option when creating a custom connector via NewConnector, which overrides the DSN value as implemented in lines 77-91 of driver_db.go.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →