Skip to content
spockIt’s only logical !
Non-normative guide. This page shows how to use Spock; the v0 specification governs where they disagree. Spock is pre-1.0 and interfaces may change β€” see project status.

Quickstart

In about ten minutes you will scaffold a project, declare a two-table authority, serve it, read from it over REST, write to it over GraphQL, hit your first derived error, and read the contract that predicted it.

This guide requires spock@0.5.4 or later. 0.5.4 is the first npm release that packages the strict Uhura 0.4 client used by the full-stack scaffold; framework releases 0.5.0 through 0.5.3 carry the retired frontend and cannot run these client files, while releases through 0.4.0 have no framework client.

Scaffold a project

Terminal window
npm install --global spock@0.5.4
spock --version
spock new demo
cd demo
demo/
β”œβ”€β”€ spock.toml
β”œβ”€β”€ backend/
β”‚ └── app.spock
└── client/
β”œβ”€β”€ uhura.toml
β”œβ”€β”€ host.toml
β”œβ”€β”€ machine.uhura
β”œβ”€β”€ ui.uhura
└── evidence.uhura

This is a framework project: a spock.toml manifest composing a Spock backend with an Uhura client. The scaffolded backend/app.spock is deliberately empty β€” a project whose authority is not designed yet can already serve its client, health, and status surfaces. This page fills the backend; the client half has its own page.

Declare the authority

Replace the contents of backend/app.spock with a complete program:

auth table user {
key id: uuid = auto
username: text unique
}
table post {
key id: uuid = auto
author: user
caption: text
at: timestamp = now
}
seed {
ada = user { username: "ada" }
lin = user { username: "lin" }
post { author: ada, caption: "hello from the authority" }
post { author: lin, caption: "works on my machine" }
}

Two tables and a seed. auth table user makes user the program’s anchor β€” the one table identity references point at. author: user is a reference; = auto and = now are defaults; unique is a constraint the whole surface will enforce. Field syntax, types, and constraints are covered in Tables, types, and defaults. The seed block populates the database through the ordinary write path on every start β€” seed replay, the reason a prototype always boots into believable state.

Check it

Terminal window
spock check
ok: project `demo` β€” 2 table(s), 0 record(s), 0 fn(s), 4 seed row(s), 2 preview(s), 1 replay-derived preview(s), 1 unchecked link(s), 1 warning(s)
warning: link: application-owned provider adapter code remains unchecked

check does more than parse: it materializes the schema in memory, validates every declaration, and replays the seed through the runtime write path. The counts after the seed rows β€” previews, links, the warning β€” describe the Uhura client half of the project. Spock checks that client as part of the same project: an invalid Uhura machine, Web UI, evidence corpus, host entry, or provider contract makes the whole spock check fail.

Serve it

Terminal window
spock dev
warning: backend inputs (including referenced seed assets) and spock.toml topology changes are observed but not applied; restart `spock dev` to reconstruct backend state from seed
listening on http://127.0.0.1:4000
GET / Uhura Editor
GET /play Uhura Play
GET /~studio Spock Studio
GET /~contract active Spock contract
GET /~project/status framework generation status
GET /~health aggregate readiness
* /rest/v1/* authority REST and RPC
POST /graphql/v1 GraphQL when the contract is non-empty

One process, one origin: the root path serves the Uhura Editor, and the authority protocols mount beside it. The warning is the development doctrine in one line β€” state is disposable, so backend edits take effect by restarting and replaying the seed, never by migrating in place.

First read

Terminal window
curl -sS http://127.0.0.1:4000/rest/v1/user
{
"rows": [
{ "id": "019f751c-3a75-7cb3-ba39-92e36fb6dd87", "username": "ada" },
{ "id": "019f751c-3a75-7cb3-ba39-92fb2cbe32ca", "username": "lin" }
]
}

You declared no route and no serializer. Every table receives a derived read/write surface β€” the floor β€” with list and by-key reads over REST, ordered by key ascending.

First write

Open http://127.0.0.1:4000/graphql/v1 in a browser: GET serves GraphiQL, with the full derived schema behind introspection. Run an insert:

mutation {
insert_user_one(object: { username: "grace" }) {
id
username
}
}
{
"data": {
"insert_user_one": {
"id": "019f751c-7c1d-7b83-a31f-a5201b4d2ebe",
"username": "grace"
}
}
}

insert_user_one was derived from the table declaration, in the Hasura-mirrored dialect specified in the GraphQL specification β€” standard GraphQL tooling consumes it unmodified.

First derived error

Run the same mutation again:

{
"data": null,
"errors": [
{
"message": "user.username is already taken",
"locations": [{ "line": 1, "column": 12 }],
"extensions": {
"code": "user_username_taken",
"kind": "unique",
"table": "user",
"fields": ["username"]
}
}
]
}

user_username_taken was minted at compile time from the unique constraint on username β€” the code existed in the contract before any request was made, and generated clients ship it as a typed union rather than parsing a message string. Derived errors are the product surface of your constraints, not incidental runtime strings; The derived API catalogs the kinds.

The contract

Everything above was derived from one artifact, served verbatim:

Terminal window
curl -sS http://127.0.0.1:4000/~contract

The contract is the compiled JSON the program produces: its tables, fields, seed, and β€” the part the previous section exercised β€” each table’s derived errors, declared before any request:

[
{ "code": "user_already_exists", "kind": "key", "fields": ["id"], "status": 409 },
{ "code": "user_username_taken", "kind": "unique", "fields": ["username"], "status": 409 },
{ "code": "user_username_required", "kind": "required", "fields": ["username"], "status": 422 },
{ "code": "user_restricted", "kind": "restricted", "fields": [], "status": 409 }
]

That array is tables[0].errors β€” the failure surface of the user table, enumerated by the compiler. The contract’s shape is normative in the v0 specification, Β§6; spock gen types turns it into TypeScript when you want the codes as literal unions.

Where next

For the backend on its own:

  • The tutorial β€” build a mini-Instagram backend: functions, refusals, personas, and the deliberate surface.
  • The language guides β€” tables onward: types, references, seeds, functions, and the actor seam.
  • The reference β€” endpoint tables, error codes, CLI commands, and the manifest.

For the full stack with Uhura:

  • The tutorial still comes first β€” the authority is the same either way.
  • Uhura β€” what the client language owns, and the Editor and Play surfaces you saw at /.
  • Examples β€” the portfolio, including the canonical full-stack Instagram project.