Build Kimberlite applications in Go with simplicity and performance.
Prerequisites
- Go 1.21 or later
- Running Kimberlite cluster (see Start)
Install
go get github.com/kimberlitedb/kimberlite-go
Quick Verification
Create main.go:
package main
import (
"fmt"
kimberlite "github.com/kimberlitedb/kimberlite-go"
)
func main() {
fmt.Println("Kimberlite Go client imported successfully!")
fmt.Printf("Version: %s\n", kimberlite.Version)
}
Run it:
go run main.go
Sample Projects
Basic: Create Table and Query Data
package main
import (
"context"
"fmt"
"log"
kimberlite "github.com/kimberlitedb/kimberlite-go"
)
func main() {
ctx := context.Background()
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
_, err = client.Exec(ctx, `
CREATE TABLE users (
id INT PRIMARY KEY,
email TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`)
if err != nil {
log.Fatal(err)
}
_, err = client.Exec(ctx,
"INSERT INTO users (id, email) VALUES (?, ?)",
1, "alice@example.com",
)
if err != nil {
log.Fatal(err)
}
rows, err := client.Query(ctx, "SELECT * FROM users")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var email string
var createdAt time.Time
if err := rows.Scan(&id, &email, &createdAt); err != nil {
log.Fatal(err)
}
fmt.Printf("User %d: %s\n", id, email)
}
}
Compliance: Enable RBAC and Test Access Control
package main
import (
"context"
"fmt"
"log"
kimberlite "github.com/kimberlitedb/kimberlite-go"
)
func main() {
ctx := context.Background()
adminClient, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
User: "admin",
Password: "admin-password",
})
if err != nil {
log.Fatal(err)
}
defer adminClient.Close()
_, err = adminClient.Exec(ctx, `
CREATE ROLE data_analyst;
GRANT SELECT ON patients TO data_analyst;
`)
if err != nil {
log.Fatal(err)
}
_, err = adminClient.Exec(ctx, `
CREATE USER analyst1
WITH PASSWORD 'analyst-password'
WITH ROLE data_analyst;
`)
if err != nil {
log.Fatal(err)
}
analystClient, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
User: "analyst1",
Password: "analyst-password",
})
if err != nil {
log.Fatal(err)
}
defer analystClient.Close()
rows, err := analystClient.Query(ctx, "SELECT * FROM patients")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
count := 0
for rows.Next() {
count++
}
fmt.Printf("Found %d patients\n", count)
_, err = analystClient.Exec(ctx,
"INSERT INTO patients VALUES (99, 'Unauthorized', '000-00-0000')",
)
if err != nil {
if kimberlite.IsPermissionDenied(err) {
fmt.Printf("Access denied: %v\n", err)
} else {
log.Fatal(err)
}
}
}
Multi-Tenant: Tenant Isolation Example
package main
import (
"context"
"fmt"
"log"
kimberlite "github.com/kimberlitedb/kimberlite-go"
)
func main() {
ctx := context.Background()
tenant1Client, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
TenantID: 1,
})
if err != nil {
log.Fatal(err)
}
defer tenant1Client.Close()
_, err = tenant1Client.Exec(ctx, `
CREATE TABLE orders (id INT, customer TEXT, amount DECIMAL);
INSERT INTO orders VALUES (1, 'Alice', 99.99);
`)
if err != nil {
log.Fatal(err)
}
tenant2Client, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
TenantID: 2,
})
if err != nil {
log.Fatal(err)
}
defer tenant2Client.Close()
rows, err := tenant2Client.Query(ctx, "SELECT * FROM orders")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
count := 0
for rows.Next() {
count++
}
fmt.Printf("Tenant 2 sees %d orders\n", count)
_, err = tenant2Client.Exec(ctx, "INSERT INTO orders VALUES (1, 'Bob', 149.99)")
if err != nil {
log.Fatal(err)
}
rows, err = tenant2Client.Query(ctx, "SELECT * FROM orders")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
count = 0
for rows.Next() {
count++
}
fmt.Printf("Tenant 2 sees %d orders\n", count) }
Compliance: Data Classification and Masking
package main
import (
"context"
"fmt"
"log"
kimberlite "github.com/kimberlitedb/kimberlite-go"
)
func main() {
ctx := context.Background()
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
_, err = client.Exec(ctx, `
CREATE TABLE patients (
id INT PRIMARY KEY,
name TEXT NOT NULL,
ssn TEXT NOT NULL,
diagnosis TEXT
);
`)
if err != nil {
log.Fatal(err)
}
_, err = client.Exec(ctx,
"ALTER TABLE patients MODIFY COLUMN ssn SET CLASSIFICATION 'PHI'",
)
if err != nil {
log.Fatal(err)
}
_, err = client.Exec(ctx,
"ALTER TABLE patients MODIFY COLUMN diagnosis SET CLASSIFICATION 'MEDICAL'",
)
if err != nil {
log.Fatal(err)
}
_, err = client.Exec(ctx, `
INSERT INTO patients VALUES
(1, 'Alice Johnson', '123-45-6789', 'Hypertension'),
(2, 'Bob Smith', '987-65-4321', 'Diabetes');
`)
if err != nil {
log.Fatal(err)
}
_, err = client.Exec(ctx, "CREATE MASK ssn_mask ON patients.ssn USING REDACT")
if err != nil {
log.Fatal(err)
}
rows, err := client.Query(ctx, "SELECT * FROM patients")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name, ssn, diagnosis string
if err := rows.Scan(&id, &name, &ssn, &diagnosis); err != nil {
log.Fatal(err)
}
fmt.Printf("%s: SSN=%s\n", name, ssn) }
rows, err = client.Query(ctx, "SHOW CLASSIFICATIONS FOR patients")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var column, classification string
if err := rows.Scan(&column, &classification); err != nil {
log.Fatal(err)
}
fmt.Printf("%s: %s\n", column, classification)
}
}
Time-Travel: Query Historical State
package main
import (
"context"
"fmt"
"log"
"time"
kimberlite "github.com/kimberlitedb/kimberlite-go"
)
func main() {
ctx := context.Background()
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
_, err = client.Exec(ctx, `
CREATE TABLE inventory (product_id INT, quantity INT);
INSERT INTO inventory VALUES (1, 100);
`)
if err != nil {
log.Fatal(err)
}
time.Sleep(2 * time.Second)
checkpoint := time.Now()
_, err = client.Exec(ctx, "UPDATE inventory SET quantity = 75 WHERE product_id = 1")
if err != nil {
log.Fatal(err)
}
rows, err := client.Query(ctx, "SELECT * FROM inventory WHERE product_id = 1")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
if rows.Next() {
var productID, quantity int
if err := rows.Scan(&productID, &quantity); err != nil {
log.Fatal(err)
}
fmt.Printf("Current quantity: %d\n", quantity) }
rows, err = client.Query(ctx,
"SELECT * FROM inventory AS OF TIMESTAMP ? WHERE product_id = 1",
checkpoint,
)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
if rows.Next() {
var productID, quantity int
if err := rows.Scan(&productID, &quantity); err != nil {
log.Fatal(err)
}
fmt.Printf("Historical quantity: %d\n", quantity) }
}
API Reference
Creating a Client
import kimberlite "github.com/kimberlitedb/kimberlite-go"
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
})
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
User: "username",
Password: "password",
})
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
TenantID: 1,
})
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
TLSEnabled: true,
TLSCACert: "/path/to/ca.pem",
})
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000", "localhost:3001", "localhost:3002"},
User: "admin",
Password: "password",
TenantID: 1,
TLSEnabled: true,
TLSCACert: "/path/to/ca.pem",
TLSClientCert: "/path/to/client.pem",
TLSClientKey: "/path/to/client-key.pem",
Timeout: 5 * time.Second,
MaxRetries: 3,
})
Executing Queries
_, err := client.Exec(ctx, `
CREATE TABLE products (
id INT PRIMARY KEY,
name TEXT NOT NULL,
price DECIMAL
)
`)
result, err := client.Exec(ctx,
"INSERT INTO products VALUES (?, ?, ?)",
1, "Widget", 19.99,
)
rowsAffected, _ := result.RowsAffected()
fmt.Printf("Inserted %d rows\n", rowsAffected)
Querying Data
rows, err := client.Query(ctx, "SELECT * FROM products")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
var price float64
if err := rows.Scan(&id, &name, &price); err != nil {
log.Fatal(err)
}
fmt.Printf("%s: $%.2f\n", name, price)
}
rows, err := client.Query(ctx,
"SELECT * FROM products WHERE price > ?",
25.0,
)
var count int
err := client.QueryRow(ctx, "SELECT COUNT(*) FROM products").Scan(&count)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Product count: %d\n", count)
Transactions
tx, err := client.Begin(ctx)
if err != nil {
log.Fatal(err)
}
_, err = tx.Exec(ctx, "UPDATE accounts SET balance = balance - 100 WHERE id = 1")
if err != nil {
tx.Rollback(ctx)
log.Fatal(err)
}
_, err = tx.Exec(ctx, "UPDATE accounts SET balance = balance + 100 WHERE id = 2")
if err != nil {
tx.Rollback(ctx)
log.Fatal(err)
}
if err := tx.Commit(ctx); err != nil {
log.Fatal(err)
}
Error Handling
import "github.com/kimberlitedb/kimberlite-go"
_, err := client.Exec(ctx, "INSERT INTO users VALUES (1, 'alice@example.com')")
if err != nil {
switch {
case kimberlite.IsConnectionError(err):
fmt.Println("Failed to connect to cluster")
case kimberlite.IsAuthenticationError(err):
fmt.Println("Invalid credentials")
case kimberlite.IsPermissionDenied(err):
fmt.Println("No permission for this operation")
case kimberlite.IsConstraintViolation(err):
fmt.Printf("Constraint violation: %v\n", err)
case kimberlite.IsQueryError(err):
fmt.Printf("Query error: %v\n", err)
default:
fmt.Printf("Error: %v\n", err)
}
}
Prepared Statements
stmt, err := client.Prepare(ctx,
"INSERT INTO logs (timestamp, message) VALUES (?, ?)",
)
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
_, err = stmt.Exec(ctx, time.Now(), "User logged in")
if err != nil {
log.Fatal(err)
}
_, err = stmt.Exec(ctx, time.Now(), "User logged out")
if err != nil {
log.Fatal(err)
}
Working with Types
import (
"database/sql"
"time"
"github.com/shopspring/decimal"
)
_, err := client.Exec(ctx, `
INSERT INTO transactions (id, amount, timestamp, metadata)
VALUES (?, ?, ?, ?)
`,
1,
decimal.NewFromFloat(99.99),
time.Now(),
`{"source": "web", "ip": "192.168.1.1"}`,
)
rows, err := client.Query(ctx, "SELECT * FROM transactions WHERE id = 1")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
if rows.Next() {
var id int
var amount decimal.Decimal
var timestamp time.Time
var metadata string
if err := rows.Scan(&id, &amount, ×tamp, &metadata); err != nil {
log.Fatal(err)
}
fmt.Printf("Amount: %s\n", amount)
fmt.Printf("Timestamp: %s\n", timestamp)
fmt.Printf("Metadata: %s\n", metadata)
}
Testing
Use the standard Go testing package:
package main
import (
"context"
"testing"
kimberlite "github.com/kimberlitedb/kimberlite-go"
)
func setup(t *testing.T) *kimberlite.Client {
ctx := context.Background()
client, err := kimberlite.Connect(ctx, &kimberlite.Config{
Addresses: []string{"localhost:3000"},
})
if err != nil {
t.Fatal(err)
}
rows, err := client.Query(ctx, "SHOW TABLES")
if err != nil {
t.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
t.Fatal(err)
}
_, err = client.Exec(ctx, "DROP TABLE IF EXISTS "+name)
if err != nil {
t.Fatal(err)
}
}
return client
}
func TestCreateTable(t *testing.T) {
client := setup(t)
defer client.Close()
ctx := context.Background()
_, err := client.Exec(ctx, `
CREATE TABLE test_table (
id INT PRIMARY KEY,
name TEXT NOT NULL
)
`)
if err != nil {
t.Fatal(err)
}
rows, err := client.Query(ctx, "SHOW TABLES")
if err != nil {
t.Fatal(err)
}
defer rows.Close()
found := false
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
t.Fatal(err)
}
if name == "test_table" {
found = true
break
}
}
if !found {
t.Error("Table test_table not found")
}
}
func TestInsertAndQuery(t *testing.T) {
client := setup(t)
defer client.Close()
ctx := context.Background()
_, err := client.Exec(ctx, "CREATE TABLE users (id INT, email TEXT)")
if err != nil {
t.Fatal(err)
}
_, err = client.Exec(ctx,
"INSERT INTO users VALUES (?, ?)",
1, "test@example.com",
)
if err != nil {
t.Fatal(err)
}
rows, err := client.Query(ctx, "SELECT * FROM users WHERE id = 1")
if err != nil {
t.Fatal(err)
}
defer rows.Close()
if !rows.Next() {
t.Fatal("Expected 1 row, got 0")
}
var id int
var email string
if err := rows.Scan(&id, &email); err != nil {
t.Fatal(err)
}
if email != "test@example.com" {
t.Errorf("Expected email 'test@example.com', got '%s'", email)
}
}
Examples
Complete example applications are available in the repository:
examples/go/basic/ - Simple CRUD application
examples/go/compliance/ - HIPAA-compliant healthcare app
examples/go/multi-tenant/ - Multi-tenant SaaS application
examples/go/http/ - HTTP REST API with net/http
examples/go/grpc/ - gRPC service
Next Steps
Further Reading