Skip to content

zoobz-io/edamame

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

edamame

CI Status codecov Go Report Card CodeQL Go Reference License Go Version Release

Statement-driven query exec for Go.

Define database queries as typed statements, execute them without magic strings.

Queries as Data

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.

Install

go get github.com/zoobz-io/edamame

Requires Go 1.24+. Supports PostgreSQL, MariaDB, SQLite, and SQL Server via astql.

Quick Start

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))
}

Capabilities

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

Why edamame?

  • 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

Structural Query Safety

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.

Documentation

  • Overview — Design philosophy and architecture

Learn

Guides

Reference

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT License — see LICENSE for details.