04-contracts/

directory
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

04-contracts

Contracts() decides where an entity, a field or a query shows up. Four entities, one for each combination.

  • 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

Four entities

A chess club. sqlite/ent/schema:

Entity Contracts Table Service
Match SQLC(), PROTO() yes create, get, delete, list
Player SQLC(), PROTO().ReadOnly() yes get, list
Standing PROTO() no list
Audit SQLC() yes none

Check it against the generated files. schema.sql has three tables and no standing. schema.proto has three messages and no Audit.

Match is the normal case. Both contracts, read and write.

Player is the roster. The proto contract is ReadOnly(), so there is no create, update or delete rpc. query.Create() is still declared and still becomes a database query — the server calls it in SeedRoster. The read only contract only removes the rpc.

It is also the entity with a natural key:

func (Player) Indexes() []entlite.Index {
	return []entlite.Index{
		index.Primary("name"),
	}
}

A club roster is identified by the player name, and Match already refers to players that way, by name and not by id. So the name is the primary key and there is no id column at all — index.Primary replaces the one entlite would otherwise generate. Everything keyed by the primary key follows it: the column is PRIMARY KEY in schema.sql, query.Get() becomes GetPlayerByName with a GetByName rpc, and CreatePlayer returns only an error, because there is no generated id left for the database to hand back.

Standing is a league table counted from matches on every request. There is no standing table. The entity exists to give the response a typed shape. Its id field carries the rank.

Audit is an internal log. No proto message, no service. The server writes rows and reads them back through a plain HTTP endpoint, so you can see they exist without exposing them.

Narrowing a single query

An entity's contracts are the default for its queries. A query can take less.

query.DeleteAll().Contracts(entlite.SQLC())   // Match, end of season cleanup

Match has both contracts, so its other queries get an rpc. This one does not. It is a database query the server calls itself.

Narrowing a single field

Same idea on fields:

field.Time("created_at").Contracts(entlite.SQLC(), entlite.PROTO().ReadOnly())

The column exists and the field is in the response. It is not in create or update requests, because DefaultFunc(time.Now) sets it on the server.

Run

cd sqlite
make run     # serves on :8080

The page has a panel per entity. The Player one is read only: get by name and list the roster.

Directories

Path Synopsis
ent

Jump to

Keyboard shortcuts

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