grain

module
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 21, 2026 License: Apache-2.0

README

Grain

Grain is a type-safe Object-Relational Mapping (ORM) for Go, built on pgx. It targets PostgreSQL only.

  • Type-safe schema — tables and columns defined once in Go; column binds are checked at startup, not on first SQL error.
  • Snapshot-driven migrations — edit your schema, then grain generate diffs it against the last applied snapshot and writes the SQL for you.
  • Fluent query builder — SELECT/INSERT/UPDATE/DELETE with joins, conditions, grouping and pagination — plus subqueries, CTEs, set operations (UNION/INTERSECT/EXCEPT), row-level locking (FOR UPDATE ... SKIP LOCKED), JSON/JSONB operators and table aliases; parameterized everywhere.
  • Transactions — one method that begins, commits and rolls back for you, with optional isolation level / read-only control.
  • Reflection-based scanning — rows map into plain structs via db: tags, including embedded structs, pointer fields and db:"-" skips.

Contents

Installation

Add the library and the grain CLI to your project:

go get github.com/etornam45/grain@latest
go install github.com/etornam45/grain/cmd/grain@latest   # CLI -> $(go env GOPATH)/bin/grain

The CLI is a separate binary from the library — you only need it if you manage migrations with grain generate / grain migrate.

A runnable version of everything below lives in examples/basic.

Usage Example
import "github.com/etornam45/grain/schema"

var UserStatus = schema.Enum("user_status", "active", "suspended", "banned")

var Users = schema.Table("users",
	schema.Column("id", schema.UUID()).PrimaryKey().Default("gen_random_uuid()"),
	schema.Column("name", schema.Varchar(255)).NotNull(),
	schema.Column("email", schema.Varchar(255)).NotNull().Unique(),
	schema.Column("age", schema.Int()),
	schema.Column("status", UserStatus).NotNull().Default("active"),
)
Migrations

Generate a migration from your schema code, then apply it:

grain generate -schema path/to/model "create user table"
DATABASE_URL=postgres://user:pass@localhost:5432/app grain migrate up

Command usage

usage:
  grain generate -schema <dir> [-force] [-yes] [name]
  grain migrate init                      (requires DATABASE_URL)
  grain migrate up                        (requires DATABASE_URL)
  grain migrate down [n]                  (requires DATABASE_URL)
  grain migrate redo                      (requires DATABASE_URL)
  grain migrate status                    (requires DATABASE_URL)
Querying

These snippets assume a table defined as in the Usage example, plus ctx, conn, tx and newID in scope. query is github.com/etornam45/grain/query and db is github.com/etornam45/grain/db.

  1. Inserting
var newID string
err = query.Insert(schema.Users).
	Values(map[string]any{
		"name":   "Ama",
		"email":  "ama@example.com",
		"status": "active",
	}).
	Returning(schema.Users.Col("id").String()).
	Scan(ctx, conn, &newID)
  1. Selecting
type User struct {
	ID     string `db:"users.id"`
	Name   string `db:"users.name"`
	Email  string `db:"users.email"`
	Status string `db:"users.status"`
}

users, err := query.Select[User](schema.Users.Col("id"), schema.Users.Col("name"), schema.Users.Col("email"), schema.Users.Col("status")).
	From(schema.Users).
	Where(query.Eq(schema.Users.Col("status"), "active")).
	OrderBy([]string{schema.Users.Col("name").Str()}, query.Asc).
	Limit(10).
	All(ctx, conn)
  1. Joins
rows, err := query.Select[UserOrderRow](schema.Users.Col("name"), schema.Orders.Col("total")).
	From(schema.Users).
	InnerJoin(schema.Orders, query.Eq(schema.Users.Col("id"), schema.Orders.Col("user_id"))).
	Where(query.Gt(schema.Orders.Col("total"), 100)).
	All(ctx, conn)
  1. Deleting
_, err = query.Delete(schema.Users).Where(query.Eq(schema.Users.Col("id"), newID)).Run(ctx, tx)
  1. Transactions
err = conn.Transaction(ctx, func(tx *db.Tx) error {
	_, err := query.Delete(schema.Orders)
		.Where(query.Eq(schema.Orders.Col("user_id"), newID))
		.Run(ctx, tx)
	if err != nil {
		return err
	}
	_, err = query.Delete(schema.Users)
		.Where(query.Eq(schema.Users.Col("id"), newID))
		.Run(ctx, tx)
	return err
})
Documentation

Full library reference and tutorials live in docs/:

  • Schema — tables, columns, types, enums, foreign keys, indexes
  • Query — select/insert/update/delete, conditions, joins, aggregation
  • db & scan — connections, transactions, row mapping
  • Migrations & CLI — the grain CLI, generation, apply/rollback/status
  • Tutorials — getting started, relationships
Requirements
  • Go 1.25+
  • PostgreSQL 12+
License

Grain is released under the Apache License 2.0. See LICENSE for details.

Directories

Path Synopsis
cmd
grain command
examples
basic command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL