Corteksa
GuidesOther

NUMBER FORMAT

NUMBER FORMAT

Declare which separator convention a number field is presented in. Pure presentation — no new field type, no new column, no DDL, no migration, and no feature flag: the per-field opt-in is itself the switch.

The invariant

The store and the wire are always unformatted. The column stays NUMERIC and the API keeps returning "1234.56" — dot decimal, no grouping — never "1.234,56". This option is a hint to the client about which convention to render in; the backend never formats and never parses a formatted string.

Note the value is a JSON string, not a JSON number, and always has been: the driver returns NUMERIC as a string to preserve arbitrary precision (see rollup-compute.service.ts, which coerces it explicitly for the same reason) and rowToDataRecord passes it through untouched. Nothing here changes that — worth stating because a display option is exactly where someone assumes otherwise.

That is what makes the option safe to flip on a live field: nothing about existing rows, filters, sorting, aggregation, export parsing or audit changes, so there is no migration and no window where a value can be misread.

It also mirrors the admin-level date_format preference, which is likewise stored, published, and applied entirely by the client.

The option

// PATCH /object/field/{fieldSlug}
{ "options": { "min": 0, "number_format": "comma_decimal" } }
Value1234.56 renders asConvention
dot_decimal1,234.56US / UK
comma_decimal1.234,56most of continental Europe
absentclient's own defaultevery field predating this option

The values are named after the decimal separator, not a country: the decimal mark is what actually distinguishes the two, and "European" is not one convention (France uses a space for thousands).

Validation

NumberOptionsValidator rejects anything outside that list with a 400, rather than ignoring it. An unrecognised value has to fail loudly — a client picks its separator from this string, so a typo'd "european" would save cleanly and then render in whatever the client falls back to, silently, on exactly the field someone configured on purpose.

Absent stays valid, which is what keeps every pre-existing number field working untouched.

Where it surfaces

number_format rides along in the field's options blob wherever field metadata is already returned — the field detail endpoint, the fields[] block on record reads, and public-form field metadata. No response DTO changed.

It is not in GET /object/field/manager: that grid deliberately excludes raw options (SEC-03/14) and surfaces governance facts, not schema internals.

Scope

number only — NumberOptionsValidator owns the NUMBER slot in FIELD_OPTIONS_VALIDATORS, and it is the only validator that checks this key.

currency and rating do not support it. Note what that means precisely: the registry validates the keys a type knows about, it does not reject unknown ones, so number_format set on any other type is stored and ignored rather than 400'd — the same as any other stray key in an options blob. Nothing reads it there. Don't rely on a rejection that will not come.

Client-side rendering, locale-aware input parsing, and per-viewer overrides are out of scope. Not formatting server-side is what keeps the round-trip lossless: a value that leaves as a number comes back as the same number.

Sorting a number field treats an empty value as the lowest value — see the records query builder. Blanks lead ASC and trail DESC instead of Postgres' default of ranking NULL highest.

On this page