Modifiers
The .forge field modifiers — + auto-generate, & unique, ^ indexed, and ? nullable — with placement and type rules.
Modifiers are sigils on a field's type. Three are prefix modifiers (+, &,
^) placed before the type name; one is the nullable modifier (?) placed after.
| Symbol | Name | Position | Meaning | Valid on |
|---|---|---|---|---|
+ | auto-generate | prefix | Fill on create (uuid/timestamp today; integer is a marker) | u32, u64, uuid, timestamp only |
& | unique | prefix | Value must be unique (enforced) | any type |
^ | indexed | prefix | Build an index for fast lookups | any type |
? | nullable | postfix (or prefix) | Value may be absent | any type |
+ — auto-generate#
+ marks a field ForgeDB fills for you. On create (db.create_<model>, and the REST
POST /api/<model> that routes through it), an omitted or nil +uuid gets a fresh
Uuid::new_v4() and an omitted or zero +timestamp gets the current time, so callers
never supply them. Valid only on u32, u64, uuid, and timestamp:
id: +uuid // fresh UUID on create when omitted/nil
seq: +u64 // primary-key marker — NOT yet auto-incremented (see below)
created_at: +timestamp // current time on create when omitted/zero`+` is type-restricted
+ on any other type — e.g. name: +string — is a fatal parse error. Only the four
generatable types above are allowed.
Integer `+` is a marker, not yet auto-incremented
+u32/+u64 currently only mark the primary-key / auto field — the generator does not
yet synthesize a value for them, so a create must still supply the integer id. Auto-increment
for integer keys (a monotonic, restart-safe, reuse-free counter) is planned; it is
deliberately not shipped as a subtly-wrong version that could reuse a deleted id. +uuid and
+timestamp are fully synthesized today. See
issue #187.
& — unique#
& marks a column whose values must be unique across all rows. Uniqueness is enforced:
on insert/update the generated code probes the field's index and rejects a duplicate
(HTTP 409). Applies to any type.
email: &string @email
slug: &string& builds an index too, so a unique field is also fast to look up.
^ — indexed#
^ builds a secondary index on the field for fast find_by_* lookups and for
index-served REST filters. Applies to any indexable type (not json).
slug: ^string
views: ^u64
status: ^OrderStatusCombine & and ^ freely; since & already indexes, ^& mainly documents intent.
See indexes & projections for hash-vs-ordered
index behavior.
Combining prefix modifiers#
Any combination of +, &, ^ is allowed, in any order, all before the type —
subject to the rule that + is valid only on u32/u64/uuid/timestamp:
id: +&^uuid // auto-gen + unique + indexed (auto-gen needs an eligible type)
email: ^&string @email // indexed + unique? — nullable#
? makes a field optional (may be absent / NULL). It is a postfix modifier, placed
after the type, and this is the idiomatic form. A prefix form also parses:
bio: string? // nullable string (idiomatic)
age: i32? // nullable int
avatar: ?string // prefix form — also parses
published_at: timestamp?? composes with the prefix modifiers:
external_id: +uuid? // auto-generate and nullableOn a relation, prefix ? declares an optional foreign key
(editor: ?User). See relations.
Nullable storage keeps absent distinct from zero
A nullable fixed-width field carries a 1-byte presence tag, so None and a stored 0
(or Value::Null, or an empty string) round-trip as different values.