Statement-driven query exec for Go.
Define database queries as typed statements, execute them without magic strings.
Edamame treats queries as specs—pure data structures wrapped in typed statements.
// Define statements as package-level variables
var (
QueryAll = edamame.NewQueryStatement("query-all", "Query all users", edamame.QuerySpec{})
ByStatus = edamame.NewQueryStatement("by-status", "Query users by status", edamame.QuerySpec{
Where: []edamame.ConditionSpec{{Field: "status", Operator: "=", Param: "status"}},
OrderBy: []edamame.OrderBySpec{{Field: "created_at", Direction: "desc"}},
Limit: ptr(50),
})
SelectByID = edamame.NewSelectStatement("select-by-id", "Select user by ID", edamame.SelectSpec{
Where: []edamame.ConditionSpec{{Field: "id", Operator: "=", Param: "id"}},
})
)
// Execute with type safety
users, _ := exec.ExecQuery(ctx, ByStatus, map[string]any{"status": "active"})
user, _ := exec.ExecSelect(ctx, SelectByID, map[string]any{"id": 123})Type-safe. No magic strings. Compile-time guarantees.
go get github.com/zoobz-io/edamameRequires Go 1.24+. Supports PostgreSQL, MariaDB, SQLite, and SQL Server via astql.
package main
import (
"context"
"fmt"
"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"
)
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"`
Status string `db:"status" type:"text"`
}
// Define statements
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{})
ActiveUsers = edamame.NewQueryStatement("active", "Query active users", edamame.QuerySpec{
Where: []edamame.ConditionSpec{
{Field: "status", Operator: "=", Param: "status"},
},
})
)
func main() {
db, _ := sqlx.Connect("postgres", "postgres://localhost/mydb?sslmode=disable")
ctx := context.Background()
// Create exec
exec, _ := edamame.New[User](db, "users", postgres.New())
// Execute statements
users, _ := exec.ExecQuery(ctx, QueryAll, nil)
user, _ := exec.ExecSelect(ctx, SelectByID, map[string]any{"id": 1})
count, _ := exec.ExecAggregate(ctx, CountAll, nil)
active, _ := exec.ExecQuery(ctx, ActiveUsers, map[string]any{"status": "active"})
fmt.Printf("%d users, user #1: %s, %.0f total, %d active\n", len(users), user.Name, count, len(active))
}| Feature | Description | Docs |
|---|---|---|
| Statement Types | Query, Select, Aggregate, Insert, Update, Delete | Statements |
| Generic Executor | Type-safe Executor[T] with compile-time checking |
Concepts |
| Multi-Dialect Support | PostgreSQL, MariaDB, SQLite, SQL Server via astql | Quickstart |
| Declarative Specs | Queries as pure data structures | Architecture |
| Thread-Safe Execution | Concurrent access, no shared mutable state | API |
- Type-safe — Generic
Executor[T]with compile-time safety via soy - No magic strings — Typed statements, not string keys
- Declarative — Specs are data, statements wrap them with identity
- Compile-time guarantees — Pass wrong statement type? Compiler catches it
- Thread-safe — Concurrent execution, no shared mutable state
Edamame enables a pattern: define statements once, execute them anywhere.
Your query logic lives in typed statements as package-level variables. The executor consumes them with full type safety. No string interpolation, no runtime query building, no SQL injection vectors.
// In your repository package
var FindActive = edamame.NewQueryStatement("find-active", "Find active users", edamame.QuerySpec{
Where: []edamame.ConditionSpec{{Field: "status", Operator: "=", Param: "status"}},
})
// In your service layer
users, err := exec.ExecQuery(ctx, FindActive, map[string]any{"status": "active"})Statements are the single source of truth. The database dialect handles rendering. Your code stays clean.
- Overview — Design philosophy and architecture
See CONTRIBUTING.md for guidelines.
MIT License — see LICENSE for details.