Skip to main content
View as Markdown

Count & Autonumber Fields

Two columns the engine fills in — a count of related records, and a sequence the database allocates.

Neither of these takes a value from a user. count is derived from a relationship; autonumber is allocated by the database on insert.

count

Counts the records linked through a relationship field in the same table — a rollup narrowed to the one aggregation everybody wants.

Path Kind Values Default Description
id number Unique identifier for a field within a table. Examples: 1, 2, 3, 100
name string Internal identifier for the field: the database column name, and the key used in API payloads and formulas. Use label for the name end users read.
label string External display name shown to end users, in place of the internal name. Resolution order on every surface: surface-level override, then this label, then the raw name verbatim.
description string Author-written guidance rendered beside the field (under the control on a form, beside the value in a drawer) and associated with the control via aria-describedby. Unlike a placeholder it persists once the user starts typing.
required boolean Rejects a record whose value for this field is missing or empty, both through the API and in any generated form.
unique boolean Rejects a record whose value for this field is already used by another record in the same table.
indexed boolean Adds a database index on this field, so filtering and sorting on it stay fast as the table grows, at the cost of slightly slower writes.
type enum count Constant value 'count' for type discrimination in discriminated unions
relationshipField string Name of the relationship field in the same table to count linked records from

filters

Path Kind Values Default Description
filters object Filters to apply when counting linked records
filters.field string Field the condition is evaluated against.
filters.operator string How the field value is compared, such as equals, greaterThan or contains.
filters.value unknown Value compared against, when the operator needs one. isEmpty and isNotEmpty need none; in and notIn take a list.
filters.and array Conditions that must ALL hold for a record to be kept.
filters.and[] object (truncated) A filter condition or a logical group (and/or) of filter nodes.
filters.or array Conditions of which at least one must hold for a record to be kept.
filters.or[] object (truncated) A filter condition or a logical group (and/or) of filter nodes.
app.yaml
- {
    id: 2,
    name: completed_task_count,
    type: count,
    relationshipField: tasks,
    filters: { field: status, operator: equals, value: completed },
  }

relationshipField must name an actual relationship field on the same table. That is checked when the configuration is decoded, so a typo or a reference to a field of another type stops the boot rather than counting nothing forever.

filters narrows what is counted, using the same condition grammar a view's filters use. Without it, every linked record counts — except a soft-deleted one, which never counts. Trashing a linked record lowers the count immediately, and restoring it raises the count again.

autonumber

A database-assigned auto-incrementing integer — the field type behind invoice and order references. The column is a sequence, so the value is allocated by the database on insert and is never supplied by the client.

Path Kind Values Default Description
id number Unique identifier for a field within a table. Examples: 1, 2, 3, 100
name string Internal identifier for the field: the database column name, and the key used in API payloads and formulas. Use label for the name end users read.
label string External display name shown to end users, in place of the internal name. Resolution order on every surface: surface-level override, then this label, then the raw name verbatim.
description string Author-written guidance rendered beside the field (under the control on a form, beside the value in a drawer) and associated with the control via aria-describedby. Unlike a placeholder it persists once the user starts typing.
required boolean Rejects a record whose value for this field is missing or empty, both through the API and in any generated form.
unique boolean Rejects a record whose value for this field is already used by another record in the same table.
indexed boolean Adds a database index on this field, so filtering and sorting on it stay fast as the table grows, at the cost of slightly slower writes.
type enum autonumber Constant value 'autonumber' for type discrimination in discriminated unions
app.yaml
- { id: 3, name: invoice_number, type: autonumber }

That produces 1, 2, 3, and so on. autonumber takes no type-specific options at all: there is no prefix, no starting offset and no zero-padding. For a human-facing reference such as INV-01000, add a formula field that composes the number with the prefix and padding you want. That keeps presentation in one place and leaves the underlying sequence untouched, so the format can change without renumbering anything.

A sequence also does not promise to be gap-free. A transaction that allocates a number and then rolls back has still consumed it, which is correct — reusing it would let two records carry the same reference at different times.

Both recompute rather than store

count recomputes when the links it counts change. autonumber is allocated once and then fixed. Neither is writable through the records API or a form: a write naming one is refused with Cannot write to readonly field '<name>' rather than accepted and discarded, and the refusal names every readonly column the request touched, not just the first.

Behaviour

Count Fields

  • Computes count of related records
  • Returns zero when no records are linked
  • Auto-updates when linked records change
  • Counts records for multiple relationship fields on the same table
  • Applies conditions to filter counted records (a single flat filter; nested AND/OR is not asserted)
  • Rejects a count field when relationshipField does not exist in the same table
  • Rejects a count field when relationshipField is not a relationship type
  • Resolves FK column via reciprocalField when foreignKey is omitted
  • User can complete full count workflow (regression)
  • Infers the FK column when the child has exactly one relationship back to the parent (no foreignKey, no reciprocalField)
  • Rejects the config at validation time when the child has two relationships back to the parent, naming the candidates
  • Exposes a count as a JSON-serialisable scalar, a string on Postgres and a number on SQLite
  • Developer counts linked records without declaring a foreignKey

Autonumber Fields

  • Creates PostgreSQL SERIAL/BIGSERIAL column
  • Auto-increments on each new record
  • Is immutable after creation: a manual UPDATE is silently reverted, never raised
  • Is always NOT NULL (automatically generated)
  • Creates a unique index automatically
  • User can complete full autonumber workflow (regression)

Last updated September 23, 2026

This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.

Built with Sovrium