entlite

module
v0.0.7 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: MIT

README

Entlite

Entity-first generator for SQLC and Proto files. Maps DB and Protobuf types automatically to maintain a single source of truth in Go services.

Documentation: docs/getting started, reference, examples.

TODO

  • Wire buf lint into the pipeline, it needs the proto package and rpc response type decisions first

  • Implement DefaultFunc for sql generation

  • Explore google AIP https://google.aip.dev/ - read it through first and decide which parts fit, query and rpc naming is the open one

  • Add mcp for visual testing of examples/

  • Fix Optional() with Validate() - generated code passes a pointer to a value func and does not compile

  • Fix Time().Optional() on postgres/mysql - sqlToGo/goFromSQL have no time case, sqlc gives sql.NullTime but the wrapper declares *time.Time, so the converter does not compile. Needs a NullTimeToPtr helper

  • Add postgres and mysql optional examples

  • Fix filter.Optional() - it only makes the proto field optional, the sql keeps a plain comparison, so an omitted value filters on the zero value instead of being skipped. Needs sqlc.narg() with an IS NULL guard. The doc comment and the 03-optional readme both claim it is skipped

  • Implement Queries Having()

  • Aggregate a row count per group, Count() already means the total next to a page

  • Min()/Max() of a time column, sqlite cannot cast a timestamp back, postgres and mysql can

  • DeleteBy(...) query

  • Create queries example with complicated edge case queries

  • Figure out enums

  • Create /internal/naming/ to have in one place consistant naming

  • Figure out migration

Folder structure

ent/
├── schema/            # you write: one file per entity
├── logic/             # you write: Go funcs for DefaultFunc and Validate
├── contract/          # the input for sqlc and buf
│   ├── proto/         # generated: schema.proto, plus your custom .proto
│   └── sqlc/          # generated: schema.sql, queries.sql, plus your custom .sql
├── gen/               # the output, rewritten on every run
│   ├── db/            # generated: typed wrapper, pointers instead of null types
│   │   └── internal/  # generated: raw sqlc output, never import it
│   ├── pb/            # generated: proto messages and the connect service
│   └── ts/            # generated: TypeScript client
├── buf.yaml
├── buf.gen.yaml
├── buf.lock
├── sqlc.yaml          # dialect and sqlc settings
└── generate.go        # the go:generate lines that run the pipeline

Get started

sql dialect flag: postgresql (default) or sqlite or mysql arguments: entity names

go run github.com/guntisdev/entlite/cmd/entlite new --dialect sqlite User Post

Examples

Example Teaches
01-basic-entity Every field type: String, Int, Int64, Float, Bool, Byte, Time, JSON
Field options: Unique(), Optional(), Immutable(), Default(), DefaultFunc()
Validate() with your own Go function from the logic package
Field level Contracts(): a password that clients write but never read, timestamps they read but never write
Ready made queries: DefaultCRUD(), CreateBulk(), ListAll(), DeleteAll()
CreateBulk().Upsert("email"): re-importing a row overwrites it instead of failing on the unique email
Queries by field: GetBy("email"), ListBy("is_active"), and Name() to rename one
Filters: filter.Range() and filter.Search()
Indexes: multi column index.Asc() / index.Desc(), mixed sort order, Unique(), Name()
The same schema on three dialects: sqlite, postgresql, mysql
02-custom Hand-written custom.sql and custom.proto live beside the generated files and survive regeneration
A hand-written service that reuses a generated proto message
Virtual fields: Contracts(entlite.PROTO()) gives a field with no column, filled in by the server
Choosing the key type per entity: field.Int64("id") on a high volume table
A foreign key follows the type of the entity it points at
Query level Contracts(): a query that stays in the database layer and gets no rpc
Two entities in one schema
A multi-word entity name: SensorReading becomes the table sensor_reading, which sqlc reads back as the Go type SensorReading
03-optional Optional() on every type that supports it, one field each
Bool cannot be optional — use Default(false) instead
An optional field becomes optional in proto and a nullable column in SQL
filter.Eq("is_featured").Optional() — a required field used as an optional filter
A string uuid primary key: Immutable(), DefaultFunc() and PROTO().ReadOnly()
Server generated fields drop out of create and update requests
Create().Upsert("slug").Ignore() — a taken slug keeps the stored row instead of failing
04-contracts SQLC() only — a table with no proto message and no service
PROTO() only — a message and a service with no table
Both — a table served to clients
PROTO().ReadOnly() — a table and a service, but no write rpc
Query level Contracts(): a query the server runs that gets no rpc
Field level PROTO().ReadOnly() for server managed timestamps
Contracts on an entity are the default; a field or query can narrow them
index.Primary("name") — a natural key, the entity gets no generated id column
05-queries Ready made queries: Create(), CreateBulk(), Get(), Update(), Delete(), DeleteAll(), ListAll()
GetBy("commit_sha") and ListBy("branch") — the fields you name become = filters
Filters spell out the operator: Eq is =, Range is a lower and upper bound, Search is LIKE
Asc() and Desc() chained, the chain order is the order of the ORDER BY columns
Limit()/Offset() take their values from the caller, a fixed Limit(10) stays in the SQL
Count() returns the total before LIMIT, next to the page of rows
Distinct("branch") returns column values, not rows, so the method gives []string
Distinct("branch", "status") dedupes on the tuple and returns the query's own row struct
GroupBy("branch") with Sum() returns one row per group, in the query's own row struct
Sum(), Avg(), Min(), Max() without a GroupBy fold the whole table into one row
Contracts(entlite.SQLC()) on a query keeps it out of the API, only the database method is generated
A comment directly above a query becomes the comment of the generated SQL and rpc

Launch example

Each example has a Makefile that generates types, bundles the JavaScript and starts the web server

cd examples/01-basic-entity/sqlite
make run

The postgresql and mysql variants run their database from docker-compose.yml, make run starts it and waits until it is ready. Use make down to stop it and drop its data

cd examples/01-basic-entity/postgresql
make run

Individual steps are available as make gen, make web and (for postgresql/mysql) make db. make clean removes node_modules, the bundle and the database

Doing it by hand, generate types

cd examples/01-basic-entity/sqlite
cd ent/
go generate

Build JavaScript

cd web/
npm install
npm run build

Run go web server

go run main.go

Release

# Tag and push version
git tag v0.0.3
git push origin v0.0.3

# Force Go proxy cache update
GOPROXY=https://proxy.golang.org GOPRIVATE= go list -m github.com/guntisdev/entlite@v0.0.3

# Update CLI tool & library dependencies in projects
go install github.com/guntisdev/entlite/cmd/entlite@v0.0.3
go get github.com/guntisdev/entlite@v0.0.3

Directories

Path Synopsis
cmd
entlite command
examples
internal
docs
Package docs builds the entlite documentation from the source tree.
Package docs builds the entlite documentation from the source tree.
docs/cmd command
naming
naming rules that turn a schema into sqlc, proto, sqlc go, proto go, typescript
naming rules that turn a schema into sqlc, proto, sqlc go, proto go, typescript
pkg
entlite
Package entlite holds the core types of the schema dsl.
Package entlite holds the core types of the schema dsl.
entlite/field
Package field holds the field builders used to describe an entity schema.
Package field holds the field builders used to describe an entity schema.
entlite/filter
Package filter holds the filters used by a ListBy query.
Package filter holds the filters used by a ListBy query.
entlite/index
Package index holds the index builders used in an entity schema.
Package index holds the index builders used in an entity schema.
entlite/query
Package query holds the query builders used in an entity schema.
Package query holds the query builders used in an entity schema.

Jump to

Keyboard shortcuts

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