Skip to content

Go `database/sql` client to interact with Cloudflare D1 database

License

Notifications You must be signed in to change notification settings

SyneHQ/d1_go_sql

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

D1 Go SQL Driver

Go

A production-grade Go database/sql driver for Cloudflare D1, enabling seamless integration with Go's standard database interfaces.

Features

  • âś… Full database/sql interface implementation
  • âś… Official Cloudflare Go SDK (v6) integration
  • âś… Connection pooling support
  • âś… Prepared statements with parameter binding
  • âś… Transaction support
  • âś… Proper type conversion (string, int64, float64, bool, []byte, time.Time, nil)
  • âś… Context-aware operations
  • âś… Metadata commands and functions (LIST DATABASES, current_database, current_user, version, connection_id)
  • âś… Comprehensive error handling
  • âś… Production-ready with best practices

Requirements

  • Go 1.22 or later

Installation

go get github.com/synehq/d1_go_sql

Quick Start

package main

import (
    "database/sql"
    "log"
    
    _ "github.com/synehq/d1_go_sql"
)

func main() {
    // Open database connection
    // Format: d1://accountID:apiToken@databaseID
    db, err := sql.Open("d1", "d1://your-account-id:your-api-token@your-database-id")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    
    // Execute queries
    rows, err := db.Query("SELECT * FROM users WHERE age > ?", 18)
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()
    
    for rows.Next() {
        var id int
        var name string
        var age int
        if err := rows.Scan(&id, &name, &age); err != nil {
            log.Fatal(err)
        }
        log.Printf("User: %d, %s, %d\n", id, name, age)
    }
}

Connection String Format

The driver accepts connection strings in the following format:

d1://accountID:apiToken@databaseID

Or using DSN parameters:

d1://accountID:apiToken@databaseID?timeout=30s

Parameters

  • accountID: Your Cloudflare account ID
  • apiToken: Your Cloudflare API token
  • databaseID: Your D1 database ID
  • timeout: (Optional) Request timeout duration (default: 30s)

Usage Examples

Basic Queries

// Query single row
var name string
err := db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name)

// Query multiple rows
rows, err := db.Query("SELECT id, name FROM users")
defer rows.Close()

for rows.Next() {
    var id int
    var name string
    rows.Scan(&id, &name)
}

Prepared Statements

stmt, err := db.Prepare("INSERT INTO users (name, age) VALUES (?, ?)")
if err != nil {
    log.Fatal(err)
}
defer stmt.Close()

result, err := stmt.Exec("Alice", 30)
if err != nil {
    log.Fatal(err)
}

lastID, _ := result.LastInsertId()
affected, _ := result.RowsAffected()

Transactions

tx, err := db.Begin()
if err != nil {
    log.Fatal(err)
}

_, err = tx.Exec("INSERT INTO users (name) VALUES (?)", "Bob")
if err != nil {
    tx.Rollback()
    log.Fatal(err)
}

err = tx.Commit()
if err != nil {
    log.Fatal(err)
}

Note: Transactions are implemented using D1's batch API. All statements within a transaction are buffered and sent as a batch when Commit() is called. This means:

  • No SQL BEGIN TRANSACTION or COMMIT statements are sent to D1
  • All statements execute atomically as a batch
  • Rollback() simply discards buffered statements without sending anything to D1

Context Support

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

rows, err := db.QueryContext(ctx, "SELECT * FROM users")

Metadata Functions

The driver supports special metadata functions and commands that return connection information:

// List all databases in the account
rows, err := db.Query("LIST DATABASES")
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

for rows.Next() {
    var name, uuid, version string
    rows.Scan(&name, &uuid, &version)
    fmt.Printf("Database: %s (UUID: %s, Version: %s)\n", name, uuid, version)
}

// Get current database name
var dbName string
db.QueryRow("SELECT current_database()").Scan(&dbName)
// Returns: database-name (fetched from API)

// Get current account ID (user)
var accountID string
db.QueryRow("SELECT current_user()").Scan(&accountID)
// Returns: your-account-id

// Get driver version
var version string
db.QueryRow("SELECT version()").Scan(&version)
// Returns: D1 Go SQL Driver v0.1.0 (Cloudflare D1 - SQLite compatible)

// Get connection ID
var connID string
db.QueryRow("SELECT connection_id()").Scan(&connID)
// Returns: account-id:database-id

Supported Functions:

  • LIST DATABASES - Lists all D1 databases in the account (returns: name, uuid, version)
  • current_database() / database() - Returns the current D1 database name
  • current_user() / user() - Returns the Cloudflare account ID
  • version() - Returns driver and database version information
  • connection_id() - Returns a unique connection identifier

Notes:

  • LIST DATABASES makes an API call to fetch real database information
  • current_database() makes an API call to fetch the actual database name
  • Other functions execute instantly without API calls
  • All functions are case-insensitive

Architecture

The driver implements the following database/sql/driver interfaces:

  • Driver - Main driver registration
  • DriverContext - Context-aware driver operations
  • Connector - Connection factory
  • Conn - Database connection
  • ConnPrepareContext - Prepared statement creation
  • Stmt - Prepared statement
  • StmtExecContext - Statement execution with context
  • StmtQueryContext - Statement querying with context
  • Rows - Result set iteration
  • Result - Execution results

Type Mapping

Go Type D1 Type
nil NULL
int, int64 INTEGER
float64 REAL
bool INTEGER (0/1)
string TEXT
[]byte BLOB
time.Time TEXT (RFC3339)

Error Handling

The driver provides detailed error messages with context:

if err != nil {
    if errors.Is(err, sql.ErrNoRows) {
        // Handle no rows case
    }
    // Other error handling
}

Best Practices

  1. Always close resources: Use defer to close connections, statements, and rows
  2. Use prepared statements: For repeated queries to improve performance
  3. Use contexts: Set appropriate timeouts for operations
  4. Connection pooling: Configure SetMaxOpenConns() and SetMaxIdleConns()
  5. Error handling: Always check and handle errors appropriately

Configuration

db, err := sql.Open("d1", dsn)
if err != nil {
    log.Fatal(err)
}

// Configure connection pool
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
db.SetConnMaxIdleTime(1 * time.Minute)

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues and questions, please open an issue on GitHub.

About

Go `database/sql` client to interact with Cloudflare D1 database

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors