Quickstart
Requirements
Go 1.24 or later.
Installation
go get github.com/zoobz-io/edamame
Basic Usage
package main
import (
"context"
"fmt"
"log"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq" // or mariadb, sqlite3, mssql driver
"github.com/zoobz-io/astql/pkg/postgres" // or mariadb, sqlite, mssql
"github.com/zoobz-io/edamame"
)
// Define your model with struct tags
type User struct {
ID int `db:"id" type:"integer" constraints:"primarykey"`
Email string `db:"email" type:"text" constraints:"notnull,unique"`
Name string `db:"name" type:"text"`
Age *int `db:"age" type:"integer"`
}
// Define statements as package-level variables
var (
QueryAll = edamame.NewQueryStatement("query-all", "Query all users", edamame.QuerySpec{})
SelectByID = edamame.NewSelectStatement("select-by-id", "Select user by ID", edamame.SelectSpec{
Where: []edamame.ConditionSpec{{Field: "id", Operator: "=", Param: "id"}},
})
CountAll = edamame.NewAggregateStatement("count-all", "Count all users", edamame.AggCount, edamame.AggregateSpec{})
)
func main() {
// Connect to database (PostgreSQL shown; MariaDB, SQLite, SQL Server also supported)
db, err := sqlx.Connect("postgres", "postgres://user:pass@localhost/mydb?sslmode=disable")
if err != nil {
log.Fatal(err)
}
// Create exec
exec, err := edamame.New[User](db, "users", postgres.New())
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// Query all users
users, err := exec.ExecQuery(ctx, QueryAll, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d users\n", len(users))
// Select user by ID
user, err := exec.ExecSelect(ctx, SelectByID, map[string]any{"id": 1})
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %s\n", user.Name)
// Insert a new user
age := 25
newUser := &User{
Email: "alice@example.com",
Name: "Alice",
Age: &age,
}
inserted, err := exec.ExecInsert(ctx, newUser)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Inserted user ID: %d\n", inserted.ID)
// Count users
count, err := exec.ExecAggregate(ctx, CountAll, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total users: %.0f\n", count)
}
What's Happening
New[User](db, "users", postgres.New())creates a exec for the User type bound to the "users" table with the PostgreSQL renderer- Statements are defined as package-level variables with unique names and descriptions
ExecQueryreturns all records matching the statement's specExecSelectreturns a single record (or error if not found)ExecInsertinserts a record and returns it with generated fields (like ID)ExecAggregateruns an aggregate function and returns the result
Defining Custom Statements
// Define statements for your domain
var (
// Query for active adult users
ActiveAdults = edamame.NewQueryStatement("active-adults", "Find active users above minimum age", edamame.QuerySpec{
Where: []edamame.ConditionSpec{
{Field: "active", Operator: "=", Param: "active"},
{Field: "age", Operator: ">=", Param: "min_age"},
},
OrderBy: []edamame.OrderBySpec{
{Field: "name", Direction: "asc"},
},
})
// Update user name
UpdateName = edamame.NewUpdateStatement("update-name", "Update user name by ID", edamame.UpdateSpec{
Set: map[string]string{"name": "new_name"},
Where: []edamame.ConditionSpec{{Field: "id", Operator: "=", Param: "id"}},
})
// Delete inactive users
DeleteInactive = edamame.NewDeleteStatement("delete-inactive", "Delete inactive users", edamame.DeleteSpec{
Where: []edamame.ConditionSpec{{Field: "active", Operator: "=", Param: "active"}},
})
// Sum of ages
SumAges = edamame.NewAggregateStatement("sum-ages", "Sum all user ages", edamame.AggSum, edamame.AggregateSpec{
Field: "age",
})
)
// Use the statements
users, err := exec.ExecQuery(ctx, ActiveAdults, map[string]any{
"active": true,
"min_age": 18,
})
affected, err := exec.ExecUpdate(ctx, UpdateName, map[string]any{
"id": 123,
"new_name": "New Name",
})
deleted, err := exec.ExecDelete(ctx, DeleteInactive, map[string]any{
"active": false,
})
total, err := exec.ExecAggregate(ctx, SumAges, nil)
Statement Parameters
Statements automatically derive their parameters from the spec. You can inspect them:
// Check statement parameters
for _, param := range ActiveAdults.Params() {
fmt.Printf("Param: %s (type: %s, required: %v)\n", param.Name, param.Type, param.Required)
}
// Output:
// Param: active (type: any, required: true)
// Param: min_age (type: any, required: true)
Next Steps
- Core Concepts - Understand factories, statements, and specs
- Statement Guide - Define custom queries, updates, and more
- Testing - Test your database operations