Tables

Create and manage database tables from the CLI. Each table automatically gets REST API endpoints.

Create a table

terminal
based table create <name> <columns...>

# Example
based table create posts \
  title:text:required \
  content:text \
  status:text:default=draft \
  author_id:text:ref=users.id

Column syntax

Each column is defined as name:type[:modifier...] where modifiers are optional and can be chained.

ModifierExampleDescription
requiredtitle:text:requiredColumn cannot be null
uniqueslug:text:uniqueValues must be unique
default=valuestatus:text:default=draftDefault value when omitted
ref=table.columnauthor_id:text:ref=users.idForeign key reference

Column types

TypeDescription
textUTF-8 string
integer64-bit signed integer
real64-bit floating point
blobBinary data

Auto-added columns

Every table automatically includes id (a text primary key the server fills with a nanoid), created_at, and updated_at. You do not need to define these.

Do not declare an id column yourself — both the CLI and the API now reject it. The column grammar has no primary-key modifier, so based table create posts id:text exits with an error, and POST /admin/tables answers 400 VALIDATION_ERROR for any id column that is not declared primaryKey: true. For a custom id type — SQLite's rowid-aliased integer key, say — post to /admin/tables with { name: "id", type: "integer", primaryKey: true }. The server discovers such a key correctly.

Column modifiers survive the round trip: a DEFAULT and a UNIQUE declared at creation are re-discovered on every server start, so the REST API still applies the default and still answers 409 CONFLICT on a duplicate. A string DEFAULT may not contain a quote: it lands in the CREATE TABLE statement as a SQL literal, so status:text:default=O'Brien is rejected with 400 VALIDATION_ERROR.

Access policies

A new table starts as owner-scoped if it has a user_id (or userId) column, otherwise private. Under owner-scoped the REST API filters reads to the caller, forces ownership on writes, and rejects anon access.

terminal
based table create notes \
  user_id:text:required \
  title:text:required \
  body:text

Change a table's policy with based table access — it writes the project's config.json via PATCH /admin/tables/:name/access:

terminal
based table access <table> <owner-scoped|public-read|private>

# Example: a blog anyone can read and only the owner can write
based table access posts public-read

See REST API → Access policies for what each policy allows.

Add columns

terminal
based table add-column <table> <columns...>

# Example
based table add-column posts \
  likes:integer:default=0 \
  published_at:text

Drop columns

terminal
based table drop-column <table> <columns...>

# Example
based table drop-column posts likes published_at

Drop a table

terminal
based table drop <name>

# Example
based table drop posts

This permanently deletes the table and all its data. There is no undo.

List tables

terminal
based table list

Generate types

After creating or modifying tables, run based typegen to regenerate your TypeScript types. This keeps your SDK types in sync with the database schema.

terminal
based typegen
# writes based.d.ts — import it as "./based"

based typegen -o src/types/based.d.ts

See the SDK docs for details on using the generated types with useQuery and useMutation. Only the project owner can run typegen and the other based table commands — they all go through /admin/*.