Go Integration
Go Integration
WeftKit Standalone speaks standard wire protocols, so every existing Go database driver works without modification. This guide covers the most common drivers for each WeftKit engine.
Prerequisites
- WeftKit Standalone running and reachable (see Deployment)
- Go 1.21 or later
- A
go.modfile in your project
Module Dependencies
go// go.mod module example.com/myapp go 1.21 require ( github.com/lib/pq v1.10.9 github.com/jackc/pgx/v5 v5.6.0 gorm.io/gorm v1.25.10 gorm.io/driver/postgres v1.5.9 go.mongodb.org/mongo-driver/v2 v2.1.0 github.com/redis/go-redis/v9 v9.6.1 github.com/neo4j/neo4j-go-driver/v5 v5.22.0 github.com/aws/aws-sdk-go-v2 v1.30.3 github.com/aws/aws-sdk-go-v2/config v1.27.27 github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.4 github.com/aws/aws-sdk-go-v2/credentials v1.17.27 )
1. WeftKitRel via database/sql + lib/pq
WeftKitRel speaks the PostgreSQL v3 wire protocol. The lib/pq driver connects to it the same way it connects to PostgreSQL.
Connecting
gopackage main import ( "context" "database/sql" "fmt" "log" "time" _ "github.com/lib/pq" ) func main() { dsn := "host=localhost port=5432 user=app_user password=secret dbname=mydb sslmode=disable" db, err := sql.Open("postgres", dsn) if err != nil { log.Fatal(err) } defer db.Close() // Connection pool settings db.SetMaxOpenConns(25) db.SetMaxIdleConns(10) db.SetConnMaxLifetime(5 * time.Minute) if err := db.Ping(); err != nil { log.Fatal("cannot reach WeftKitRel:", err) } fmt.Println("connected to WeftKitRel") }
Querying
gofunc listUsers(ctx context.Context, db *sql.DB, minAge int) ([]User, error) { rows, err := db.QueryContext(ctx, "SELECT id, name, email FROM users WHERE age > $1 ORDER BY name", minAge, ) if err != nil { return nil, fmt.Errorf("query users: %w", err) } defer rows.Close() var users []User for rows.Next() { var u User if err := rows.Scan(&u.ID, &u.Name, &u.Email); err != nil { return nil, fmt.Errorf("scan user: %w", err) } users = append(users, u) } return users, rows.Err() }
Executing Writes
gofunc createUser(ctx context.Context, db *sql.DB, name, email string, age int) (int64, error) { var id int64 err := db.QueryRowContext(ctx, "INSERT INTO users (name, email, age) VALUES ($1, $2, $3) RETURNING id", name, email, age, ).Scan(&id) if err != nil { return 0, fmt.Errorf("insert user: %w", err) } return id, nil }
Transactions
gofunc transferFunds(ctx context.Context, db *sql.DB, fromID, toID int64, amount float64) error { tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable}) if err != nil { return fmt.Errorf("begin transaction: %w", err) } defer tx.Rollback() // no-op if committed _, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance - $1 WHERE id = $2", amount, fromID, ) if err != nil { return fmt.Errorf("debit: %w", err) } _, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance + $1 WHERE id = $2", amount, toID, ) if err != nil { return fmt.Errorf("credit: %w", err) } return tx.Commit() }
2. WeftKitRel via jackc/pgx/v5
pgx is a high-performance PostgreSQL driver that avoids the database/sql abstraction overhead. Use it when you need named arguments, batch queries, or the fastest possible query throughput.
Connecting with a Pool
gopackage main import ( "context" "fmt" "log" "github.com/jackc/pgx/v5/pgxpool" ) func newPool(ctx context.Context) (*pgxpool.Pool, error) { connString := "postgresql://app_user:secret@localhost:5432/mydb" config, err := pgxpool.ParseConfig(connString) if err != nil { return nil, err } config.MaxConns = 20 config.MinConns = 2 pool, err := pgxpool.NewWithConfig(ctx, config) if err != nil { return nil, fmt.Errorf("create pool: %w", err) } return pool, nil }
Single Connection
goimport "github.com/jackc/pgx/v5" conn, err := pgx.Connect(ctx, "postgresql://app_user:secret@localhost:5432/mydb") if err != nil { log.Fatal(err) } defer conn.Close(ctx)
Collecting Rows into a Struct
gotype Product struct { ID int64 Name string Price float64 } func listProducts(ctx context.Context, pool *pgxpool.Pool) ([]Product, error) { rows, err := pool.Query(ctx, "SELECT id, name, price FROM products WHERE active = true") if err != nil { return nil, err } return pgx.CollectRows(rows, pgx.RowToStructByName[Product]) }
Named Arguments
goimport "github.com/jackc/pgx/v5" func searchProducts(ctx context.Context, pool *pgxpool.Pool, category string, maxPrice float64) ([]Product, error) { rows, err := pool.Query(ctx, "SELECT id, name, price FROM products WHERE category = @category AND price <= @max_price", pgx.NamedArgs{ "category": category, "max_price": maxPrice, }, ) if err != nil { return nil, err } return pgx.CollectRows(rows, pgx.RowToStructByName[Product]) }
Batch Queries
gofunc insertProducts(ctx context.Context, pool *pgxpool.Pool, products []Product) error { batch := &pgx.Batch{} for _, p := range products { batch.Queue( "INSERT INTO products (name, price) VALUES ($1, $2)", p.Name, p.Price, ) } results := pool.SendBatch(ctx, batch) defer results.Close() for range products { if _, err := results.Exec(); err != nil { return fmt.Errorf("batch insert: %w", err) } } return nil }
3. WeftKitRel via GORM
GORM is the most popular Go ORM. Point its PostgreSQL driver at WeftKitRel.
Connecting
gopackage main import ( "log" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" ) func newDB() (*gorm.DB, error) { dsn := "host=localhost user=app_user password=secret dbname=mydb port=5432 sslmode=disable" return gorm.Open(postgres.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), }) }
Defining a Model
goimport "time" type User struct { ID uint `gorm:"primarykey"` Name string `gorm:"not null"` Email string `gorm:"uniqueIndex;not null"` Age int CreatedAt time.Time UpdatedAt time.Time Orders []Order `gorm:"foreignKey:UserID"` } type Order struct { ID uint `gorm:"primarykey"` UserID uint `gorm:"not null;index"` Total float64 `gorm:"not null"` Status string `gorm:"default:'pending'"` }
Auto-Migration
gofunc migrate(db *gorm.DB) error { return db.AutoMigrate(&User{}, &Order{}) }
CRUD Operations
go// Create user := User{Name: "Alice", Email: "alice@example.com", Age: 30} result := db.Create(&user) if result.Error != nil { return result.Error } fmt.Println("created user with ID:", user.ID) // Read by primary key var found User db.First(&found, user.ID) // Read with conditions var adults []User db.Where("age > ?", 18).Order("name").Find(&adults) // Update db.Save(&User{ID: 1, Name: "Alice Smith", Email: "alice@example.com", Age: 31}) // Update specific fields db.Model(&User{}).Where("age < ?", 18).Update("status", "minor") // Delete db.Delete(&User{}, user.ID)
Preloading Associations
govar users []User db.Preload("Orders").Where("age > ?", 18).Find(&users) for _, u := range users { fmt.Printf("%s has %d orders\n", u.Name, len(u.Orders)) }
Transactions with GORM
goerr := db.Transaction(func(tx *gorm.DB) error { if err := tx.Create(&User{Name: "Bob", Email: "bob@example.com"}).Error; err != nil { return err // triggers rollback } if err := tx.Create(&Order{UserID: 2, Total: 49.99}).Error; err != nil { return err // triggers rollback } return nil // commit })
4. WeftKitDoc via go.mongodb.org/mongo-driver/v2
WeftKitDoc speaks the MongoDB Wire protocol. The official MongoDB Go driver connects to it without any changes.
Connecting
gopackage main import ( "context" "log" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" ) func newMongoClient(ctx context.Context) (*mongo.Client, error) { uri := "mongodb://app_user:secret@localhost:27017" client, err := mongo.Connect(options.Client().ApplyURI(uri)) if err != nil { return nil, err } if err := client.Ping(ctx, nil); err != nil { return nil, err } return client, nil }
Inserting Documents
goimport ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" ) type Article struct { Title string `bson:"title"` Author string `bson:"author"` Tags []string `bson:"tags"` Views int `bson:"views"` } func insertArticles(ctx context.Context, col *mongo.Collection) error { // Single insert _, err := col.InsertOne(ctx, Article{ Title: "Getting Started with WeftKit", Author: "Alice", Tags: []string{"database", "weftkit"}, Views: 0, }) if err != nil { return err } // Bulk insert docs := []interface{}{ Article{Title: "MVCC Deep Dive", Author: "Bob", Tags: []string{"internals"}}, Article{Title: "Vector Search", Author: "Carol", Tags: []string{"ai", "search"}}, } _, err = col.InsertMany(ctx, docs) return err }
Querying Documents
go// Find one var article Article filter := bson.D{{Key: "author", Value: "Alice"}} err := col.FindOne(ctx, filter).Decode(&article) if err != nil { return err } // Find many cursor, err := col.Find(ctx, bson.D{{Key: "tags", Value: "database"}}) if err != nil { return err } defer cursor.Close(ctx) var articles []Article if err := cursor.All(ctx, &articles); err != nil { return err }
Updating Documents
gofilter := bson.D{{Key: "title", Value: "Getting Started with WeftKit"}} update := bson.D{{Key: "$set", Value: bson.D{ {Key: "views", Value: 1500}, {Key: "featured", Value: true}, }}} _, err = col.UpdateOne(ctx, filter, update)
Aggregation Pipeline
gopipeline := mongo.Pipeline{ {{Key: "$match", Value: bson.D{{Key: "views", Value: bson.D{{Key: "$gt", Value: 100}}}}}}, {{Key: "$group", Value: bson.D{ {Key: "_id", Value: "$author"}, {Key: "total_views", Value: bson.D{{Key: "$sum", Value: "$views"}}}, {Key: "article_count", Value: bson.D{{Key: "$sum", Value: 1}}}, }}}, {{Key: "$sort", Value: bson.D{{Key: "total_views", Value: -1}}}}, } cursor, err := col.Aggregate(ctx, pipeline)
Creating Indexes
goimport "go.mongodb.org/mongo-driver/v2/mongo/indexopt" indexModel := mongo.IndexModel{ Keys: bson.D{{Key: "author", Value: 1}, {Key: "views", Value: -1}}, Options: options.Index().SetUnique(false), } _, err = col.Indexes().CreateOne(ctx, indexModel)
5. WeftKitMem via go-redis/v9
WeftKitMem speaks Redis RESP3. The go-redis client connects to it directly.
Connecting
gopackage main import ( "context" "fmt" "log" "time" "github.com/redis/go-redis/v9" ) func newRedisClient() *redis.Client { return redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "secret", DB: 0, PoolSize: 10, MinIdleConns: 2, DialTimeout: 5 * time.Second, ReadTimeout: 3 * time.Second, WriteTimeout: 3 * time.Second, }) }
String Commands
goctx := context.Background() // Set with expiration err := client.Set(ctx, "session:abc123", "user_42", 30*time.Minute).Err() // Get val, err := client.Get(ctx, "session:abc123").Result() if err == redis.Nil { fmt.Println("key not found") } else if err != nil { log.Fatal(err) } else { fmt.Println("session value:", val) } // Atomic increment views, err := client.Incr(ctx, "article:42:views").Result()
Hash Commands
go// Set multiple hash fields err = client.HSet(ctx, "user:42", "name", "Alice", "email", "alice@example.com", "plan", "pro", ).Err() // Get all hash fields fields, err := client.HGetAll(ctx, "user:42").Result() fmt.Println(fields["name"], fields["plan"]) // Get specific field email, err := client.HGet(ctx, "user:42", "email").Result()
List Commands
go// Push to list client.LPush(ctx, "tasks:queue", "task_1", "task_2", "task_3") // Pop (blocking, wait up to 5 seconds) result, err := client.BLPop(ctx, 5*time.Second, "tasks:queue").Result() if err == nil { fmt.Println("processing task:", result[1]) } // Read range without removing items, err := client.LRange(ctx, "tasks:queue", 0, -1).Result()
Sorted Set Commands
go// Add members with scores client.ZAdd(ctx, "leaderboard", redis.Z{Score: 1500, Member: "alice"}, redis.Z{Score: 2200, Member: "bob"}, redis.Z{Score: 980, Member: "carol"}, ) // Get top 10 (descending) top, err := client.ZRevRangeWithScores(ctx, "leaderboard", 0, 9).Result() for _, entry := range top { fmt.Printf("%s: %.0f\n", entry.Member, entry.Score) }
Pipeline
gopipe := client.Pipeline() pipe.Set(ctx, "key:1", "value_1", 10*time.Minute) pipe.Set(ctx, "key:2", "value_2", 10*time.Minute) pipe.Incr(ctx, "pipeline:counter") cmds, err := pipe.Exec(ctx) if err != nil { log.Fatal(err) } for _, cmd := range cmds { fmt.Println(cmd.String()) }
Pub/Sub
go// Publisher go func() { for { client.Publish(ctx, "events:orders", `{"order_id":42,"status":"shipped"}`) time.Sleep(time.Second) } }() // Subscriber pubsub := client.Subscribe(ctx, "events:orders") defer pubsub.Close() ch := pubsub.Channel() for msg := range ch { fmt.Println("received:", msg.Payload) }
6. WeftKitGraph via neo4j.com/neo4j-go-driver/v5
WeftKitGraph speaks the Bolt protocol. The official Neo4j Go driver connects to it directly.
Connecting
gopackage main import ( "context" "log" "github.com/neo4j/neo4j-go-driver/v5/neo4j" ) func newGraphDriver(ctx context.Context) (neo4j.DriverWithContext, error) { uri := "bolt://localhost:7687" auth := neo4j.BasicAuth("app_user", "secret", "") driver, err := neo4j.NewDriverWithContext(uri, auth) if err != nil { return nil, err } if err := driver.VerifyConnectivity(ctx); err != nil { return nil, err } return driver, nil }
Read Session
gotype Person struct { Name string Age int } func findFriends(ctx context.Context, driver neo4j.DriverWithContext, name string) ([]Person, error) { session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead}) defer session.Close(ctx) result, err := session.ExecuteRead(ctx, func(tx neo4j.ManagedTransaction) (interface{}, error) { records, err := tx.Run(ctx, "MATCH (p:Person {name: $name})-[:FRIEND]->(f:Person) RETURN f.name AS name, f.age AS age", map[string]interface{}{"name": name}, ) if err != nil { return nil, err } var friends []Person for records.Next(ctx) { record := records.Record() name, _ := record.Get("name") age, _ := record.Get("age") friends = append(friends, Person{Name: name.(string), Age: int(age.(int64))}) } return friends, records.Err() }) if err != nil { return nil, err } return result.([]Person), nil }
Write Session
gofunc createFriendship(ctx context.Context, driver neo4j.DriverWithContext, name1, name2 string) error { session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite}) defer session.Close(ctx) _, err := session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (interface{}, error) { _, err := tx.Run(ctx, ` MERGE (a:Person {name: $name1}) MERGE (b:Person {name: $name2}) MERGE (a)-[:FRIEND]->(b) `, map[string]interface{}{"name1": name1, "name2": name2}) return nil, err }) return err }
7. WeftKitKV via AWS SDK for Go v2
WeftKitKV exposes a DynamoDB-compatible REST API. Use the AWS SDK for Go v2 with a custom endpoint pointing at WeftKit.
Connecting
gopackage main import ( "context" "log" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/dynamodb" "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" ) func newDynamoClient(ctx context.Context) (*dynamodb.Client, error) { cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1"), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider( "weftkit", // access key — any value accepted by WeftKitKV "weftkit", // secret key — any value accepted by WeftKitKV "", )), config.WithEndpointResolverWithOptions( aws.EndpointResolverWithOptionsFunc( func(service, region string, options ...interface{}) (aws.Endpoint, error) { return aws.Endpoint{URL: "http://localhost:8000"}, nil }, ), ), ) if err != nil { return nil, err } return dynamodb.NewFromConfig(cfg), nil }
PutItem
gofunc putItem(ctx context.Context, client *dynamodb.Client) error { _, err := client.PutItem(ctx, &dynamodb.PutItemInput{ TableName: aws.String("Sessions"), Item: map[string]types.AttributeValue{ "session_id": &types.AttributeValueMemberS{Value: "sess_abc123"}, "user_id": &types.AttributeValueMemberN{Value: "42"}, "expires_at": &types.AttributeValueMemberN{Value: "1735689600"}, "data": &types.AttributeValueMemberS{Value: `{"theme":"dark"}`}, }, }) return err }
GetItem
gofunc getItem(ctx context.Context, client *dynamodb.Client, sessionID string) (map[string]types.AttributeValue, error) { output, err := client.GetItem(ctx, &dynamodb.GetItemInput{ TableName: aws.String("Sessions"), Key: map[string]types.AttributeValue{ "session_id": &types.AttributeValueMemberS{Value: sessionID}, }, }) if err != nil { return nil, err } return output.Item, nil }
DeleteItem
gofunc deleteItem(ctx context.Context, client *dynamodb.Client, sessionID string) error { _, err := client.DeleteItem(ctx, &dynamodb.DeleteItemInput{ TableName: aws.String("Sessions"), Key: map[string]types.AttributeValue{ "session_id": &types.AttributeValueMemberS{Value: sessionID}, }, }) return err }
Connection String Quick Reference
| Engine | Driver | Connection String |
|---|---|---|
| WeftKitRel | lib/pq | host=localhost port=5432 user=app_user password=secret dbname=mydb sslmode=disable |
| WeftKitRel | pgx | postgresql://app_user:secret@localhost:5432/mydb |
| WeftKitRel | GORM | host=localhost user=app_user password=secret dbname=mydb port=5432 sslmode=disable |
| WeftKitDoc | mongo-driver | mongodb://app_user:secret@localhost:27017 |
| WeftKitMem | go-redis | Addr: "localhost:6379", Password: "secret" |
| WeftKitGraph | neo4j-driver | bolt://localhost:7687 |
| WeftKitKV | aws-sdk-go-v2 | http://localhost:8000 (endpoint override) |
Next Steps
- Deployment — Start and configure WeftKit Standalone
- Security — Configure TLS, JWT, and mTLS for production
- Integration Guides — Other language guides
On this page