Generate schema.lua from spacetime describe --json #14

Closed
opened 2026-08-19 07:38:08 +00:00 by yuris · 1 comment
Owner

Column order is now load-bearing: TransactionUpdate sends rows as positional arrays, so schema.lua needs columns = { ... } in the order the module declared them. Hand-maintaining that is a footgun — reorder a column in the module and every client silently mis-maps fields.

The server already publishes the answer: spacetime describe --json <db> returns the typespace with product elements in declaration order, plus an ExplicitNames section mapping source names to canonical snake_case (which is what reducer calls must use).

  • Script (tools/gen-schema) that fetches the schema over HTTP and emits a schema.lua
  • Emit reducer name constants too, so callers can't use the JS-side camelCase name by mistake
  • Document in README next to the fixture-capture flow
  • Regenerate the chat schema with it and confirm the fixture tests still pass

Related: #3 (fixtures), #2 (vertical slice).

Column **order** is now load-bearing: `TransactionUpdate` sends rows as positional arrays, so `schema.lua` needs `columns = { ... }` in the order the module declared them. Hand-maintaining that is a footgun — reorder a column in the module and every client silently mis-maps fields. The server already publishes the answer: `spacetime describe --json <db>` returns the typespace with product elements in declaration order, plus an `ExplicitNames` section mapping source names to canonical snake_case (which is what reducer calls must use). - [ ] Script (`tools/gen-schema`) that fetches the schema over HTTP and emits a `schema.lua` - [ ] Emit reducer name constants too, so callers can't use the JS-side camelCase name by mistake - [ ] Document in README next to the fixture-capture flow - [ ] Regenerate the chat schema with it and confirm the fixture tests still pass Related: #3 (fixtures), #2 (vertical slice).
Author
Owner

Closed in ad3779c

tools/gen-schema.sh <base-url> <db> reads /v1/database/<db>/schema?version=9 and emits a Lua module:

return {
	tables = {
		["message"] = { columns = { "id", "sender", "text" }, pk = "id", },
	},
	-- Canonical wire names; call reducers with these, not the module-source names.
	reducers = {
		["clear_all"] = { params = {  } },
		["send_message"] = { params = { "sender", "text" } },
	},
}

That HTTP endpoint turned out to be much friendlier than spacetime describe --json: tables[].product_type_ref indexes into typespace.types[] for the ordered elements, primary_key is a column-index array, and reducer names come back already canonical — no ExplicitNames cross-referencing needed, and no CLI or docker required to run the generator.

Made load-bearing, not optional

A generator nobody runs is worse than no generator. So:

  • tools/dev-server.sh regenerates test/fixtures/chat_schema.lua on every publish
  • test_fixtures.lua consumes that generated file instead of a hand-typed schema, and asserts the column order, pk, canonical reducer name and params

Verified the guard actually fires

Swapped sender and text in server/index.js, republished, ran the suite:

FAIL generated column order matches the module: expected id,sender,text, got id,text,sender

Reverted, suite green. So the failure mode this issue was filed against — a reordered column silently mis-mapping every row — now stops at the test instead of reaching a game.

Note for consumers

stdb.schema.new(generated.tables) is the intended usage; generated.reducers exists so callers can avoid hardcoding a name in the wrong case. pkgs.jq was added to the dev shells for the generator.

## Closed in ad3779c `tools/gen-schema.sh <base-url> <db>` reads `/v1/database/<db>/schema?version=9` and emits a Lua module: ```lua return { tables = { ["message"] = { columns = { "id", "sender", "text" }, pk = "id", }, }, -- Canonical wire names; call reducers with these, not the module-source names. reducers = { ["clear_all"] = { params = { } }, ["send_message"] = { params = { "sender", "text" } }, }, } ``` That HTTP endpoint turned out to be much friendlier than `spacetime describe --json`: `tables[].product_type_ref` indexes into `typespace.types[]` for the ordered elements, `primary_key` is a column-index array, and reducer names come back already canonical — no `ExplicitNames` cross-referencing needed, and no CLI or docker required to run the generator. ## Made load-bearing, not optional A generator nobody runs is worse than no generator. So: - `tools/dev-server.sh` regenerates `test/fixtures/chat_schema.lua` on every publish - `test_fixtures.lua` consumes that generated file instead of a hand-typed schema, and asserts the column order, pk, canonical reducer name and params ## Verified the guard actually fires Swapped `sender` and `text` in `server/index.js`, republished, ran the suite: ``` FAIL generated column order matches the module: expected id,sender,text, got id,text,sender ``` Reverted, suite green. So the failure mode this issue was filed against — a reordered column silently mis-mapping every row — now stops at the test instead of reaching a game. ## Note for consumers `stdb.schema.new(generated.tables)` is the intended usage; `generated.reducers` exists so callers can avoid hardcoding a name in the wrong case. `pkgs.jq` was added to the dev shells for the generator.
yuris closed this issue 2026-08-19 09:00:14 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/spacetimedb-defold#14
No description provided.