Skip to content
CLI
Custom Tables

Custom Tables

The hutly tables command group manages custom tables in your Hutly organization — both the table schemas (defined in YAML and created from disk) and the rows stored inside them.

Table schemas are editable in place. hutly tables push diffs your YAML against the live table and applies the difference — add or remove a column, toggle required, change a default, or add a foreign key or unique constraint, and the next push carries it out. Changing a column’s type (or its Data Dictionary link) is the one change the diff can’t express: that still requires --force-recreate, which drops the table and recreates it from the YAML, destroying every row. Rows, by contrast, are fully mutable: create, update, delete, and bulk-import them with hutly tables rows.

Every table is created with a server-assigned, auto-increment id column. Never declare your own primary-key column — id, record_id, row_id, pk, primary_key, and <table_name>_id are all rejected. A _id suffix is for a foreign key pointing at another table (name it <parent_table>_id), not a substitute primary key.

All commands operate against the organization configured in your CLI context and require valid credentials. Every command accepts --json; JSON is the default machine-readable output for read commands, and the flag is a no-op elsewhere for scripting compatibility.

Schema commands

hutly tables list

List the custom tables in the current organization.

hutly tables list
hutly tables list --limit 50 --offset 50 --pretty
Option Description
-l, --limit <n> Max results (default: 20)
-o, --offset <n> Result offset (default: 0)
--pretty Pretty-print JSON output

hutly tables get

Fetch a table’s schema plus its first page of rows (up to 100) as a single bundle.

hutly tables get applicants
hutly tables get applicants --pretty
Option Description
--pretty Pretty-print JSON output

hutly tables describe

Fetch a table’s schema only, with no rows. Use get when you also want the first page of rows.

hutly tables describe applicants --pretty
Option Description
--pretty Pretty-print JSON output

hutly tables push

Create or update a custom table from a YAML file. Table names must contain only letters, numbers, and underscores.

push is declarative: if the table doesn’t exist yet, it’s created. If it does, the CLI asks the server for a change plan — the list of steps needed to make the live table match the YAML — and applies it. Steps that destroy data (dropping a column drops its values) are flagged; the CLI prompts for confirmation before applying any of them, and --dry-run prints the plan without applying it at all.

Pass --force-recreate to drop the existing table — this deletes all of its rows — and recreate it from the YAML instead of diffing. This is still the only way to change a column’s type or its Data Dictionary link, since the diff can’t express either.

hutly tables push tables/applicants.yaml
hutly tables push tables/applicants.yaml --dry-run
hutly tables push tables/applicants.yaml --force-recreate
hutly tables push tables/applicants.yaml --force-recreate --yes
hutly tables push tables/applicants.yaml --var region=vic
Option Description
--dry-run Print the change plan without applying it
--force-recreate If the table already exists, delete it (and all rows) and recreate from this YAML
-v, --var <key=value> Set a variable value (repeatable) for YAML preprocessing
-y, --yes Skip confirmation prompts

Table schema YAML

A table YAML document has a name, an optional description, and a non-empty fields array. Each field is either a plain column or a Data Dictionary–linked column.

name: applicants
description: Rental applicants captured during screening
fields:
  - name: full_name
    type: text
    required: true
    comments: Applicant's full legal name
  - name: monthly_income
    type: numeric
    required: false
    comments: Stated gross monthly income
  - name: screened
    type: boolean
    required: false
    default: false
    comments: Whether the applicant has been screened

Plain-column fields:

Key Description
name Column name (letters, numbers, underscores)
type One of text, numeric, boolean, timestamp, jsonb, reference
required Whether the column is mandatory
default Optional default value
comments Optional column description
fk (type: reference only) name of the custom table this column points at
display (with fk only) name of a column on the parent table to show instead of the raw id — pick a text column (see below)

Foreign keys point a reference column at another custom table’s id, in the same organization, ON DELETE RESTRICT — deleting a parent row a child still references is refused. display names the parent column shown in the UI instead of the raw id; a display without an fk is an error. display must be a text column: the row picker searches it with a case-insensitive text match, so a numeric or timestamp display column can’t be searched. This is enforced server-side — a schema update naming a display column that doesn’t exist on the parent table, or that exists but isn’t text, is rejected with a 400 naming the column and the table.

name: leases
fields:
  - name: property_id     # FK -> properties.id
    type: reference
    fk: properties
    display: address_line_1
    required: true
  - ddField: rent_amount
    required: true
unique:
  - [property_id, rent_amount]

Unique constraints live in a top-level unique array, each entry a list of one or more column names. Composite constraints are the common case once foreign keys exist — the example above means one rent value per property, and unique: [[property_id, inspection_date]] would mean one inspection per property per date.

Data Dictionary columns use the ddField shorthand, linking a column to a live Hutly Data Dictionary field by its canonical forms_fields.name. The server resolves and freezes the DD snapshot (label, type, enum values) at create time, so later DD edits don’t silently break the table.

name: lease_parties
fields:
  - ddField: tenant_full_name
    required: true
  - ddField: lease_start_date
    required: false
Key Description
ddField Canonical Data Dictionary field name to link
required Whether the column is mandatory
default Optional default value
comments Optional column description

hutly tables pull

Fetch a table’s schema and write it to a YAML file. Rows are not included — manage those with hutly tables rows. DD-linked columns round-trip as the ddField shorthand so a pulled file can be pushed back without invalidating the link.

hutly tables pull applicants
hutly tables pull applicants --output ./schemas/applicants.yaml
Option Description
-o, --output <file> Output file path (defaults to ./tables/<tableName>.yaml)

hutly tables delete

Permanently delete a custom table and all of its rows. This is irreversible. The CLI prompts for confirmation; pass --yes to skip it.

hutly tables delete applicants
hutly tables delete applicants --yes
Option Description
-y, --yes Skip confirmation prompts

Row commands

Rows live under hutly tables rows <verb> <tableName>.

hutly tables rows list

List rows in a table. Pass --filter and --order multiple times to build compound queries.

hutly tables rows list applicants
hutly tables rows list applicants --limit 50 --offset 0 --pretty
hutly tables rows list applicants \
  --filter "monthly_income:gte:5000:and" \
  --filter "screened:eq:true:and" \
  --order "monthly_income:desc"
Option Description
-l, --limit <n> Max results (default: 100)
-o, --offset <n> Result offset (default: 0)
-f, --filter <segment> Filter field:op:value:join — repeatable
--order <segment> Order field:asc or field:desc — repeatable
--pretty Pretty-print JSON output

Filter segments take the form field:op:value:join:

  • op is one of eq, ne, gt, lt, gte, lte, like, ilike, in, nin
  • join is and or or, combining the segment with the next one

Order segments take the form field:asc or field:desc; repeat for multi-key ordering.

hutly tables rows create

Insert a single row. Provide the column values as a JSON object that matches the table’s field schema, either inline or from a file.

hutly tables rows create applicants --data '{"full_name":"Sam Lee","monthly_income":6200,"screened":false}'
hutly tables rows create applicants --data-file ./row.json
Option Description
--data <json> Inline JSON object matching the table’s field schema
--data-file <path> Read row JSON from a file

Pass exactly one of --data or --data-file.

hutly tables rows update

Replace the columns of a single row, identified by its numeric id (a positive integer).

hutly tables rows update applicants 42 --data '{"screened":true}'
hutly tables rows update applicants 42 --data-file ./update.json
Option Description
--data <json> Inline JSON object with the new column values
--data-file <path> Read row JSON from a file

Pass exactly one of --data or --data-file.

hutly tables rows delete

Delete a single row by its numeric id. The CLI prompts for confirmation; pass --yes to skip it.

hutly tables rows delete applicants 42
hutly tables rows delete applicants 42 --yes
Option Description
-y, --yes Skip confirmation prompts

hutly tables rows import

Bulk-insert rows from a JSON file. The file must contain a JSON array of row objects. The server validates each row independently and reports per-row errors; partial success is allowed. When any rows fail, the command prints the result envelope and exits non-zero.

hutly tables rows import applicants ./applicants-seed.json
[
  { "full_name": "Sam Lee", "monthly_income": 6200, "screened": false },
  { "full_name": "Jo Park", "monthly_income": 4800, "screened": true }
]