Cheatsheet

A one-page scannable reference to every .forge type, modifier, relation, and directive, plus a kitchen-sink schema.

A compact lookup for the whole .forge language. Follow the links for the full detail.

Scalar types#

TypeRustTSIndexable?
u32 u64 i32 i64samenumber
f64f64numberfilter/sort (no ordered index)
boolboolboolean
stringStringstring✅ (exact-match)
string(N) string(N!)String (InlineStr<N> as a key)string✅ — fixed N-character slot in the row; ! = exactly N, bare = at most. ASCII unless @utf8. Legal as an identity: Copy key, RFC 3986 pchar minus %, non-empty, no @utf8
bytes(N)[u8; N]any✅ (raw bytes, not text; struct-safe)
uuidUuidstring
timestamp, timestamp(s|ms|us)Timestampstring (RFC 3339)
decimalDecimalstring✅ (normalized key)
jsonserde_json::Valueunknown

No text type. See scalar types.

Identity#

Every model needs one: a field named id, else the first + field. id wins by name even over a + field declared above it. Legal types, and nothing else:

uuid · u32 u64 i32 i64 · timestamp(s|ms|us) (+timestamp only when named id, only at us) · string(N) string(N!) · *Model (resolves to the target's key)

Anything else — bare string, bytes(N), f64, bool, decimal, json, an enum, a struct, an array, any nullable including ?Model — is a validation error naming the field. See the identity field.

Modifiers#

SigilPositionMeaningNotes
+prefixauto-generateu32/u64/uuid/timestamp only — all fill on create when omitted. Integer autos are monotonic, not gapless, and cannot take an explicit 0; any shape (identity, &, ^, or bare) is valid. A +timestamp is a stamp unless it is named id, and id: +timestamp must be declared us
&prefixunique (enforced)any type; also indexes
^prefixindexedany indexable type
?postfix (or prefix)nullableany type

Combine freely: id: +uuid, email: ^&string, bio: string?, external: +uuid?. See modifiers.

Relations#

SyntaxKind
[Model]one-to-many (virtual)
*Modelrequired FK
?Modeloptional FK
[..] / [..] on both sidesmany-to-many (junction)
[type; N]fixed-size array (fixed-size element)
StructName / StructName?inline struct (fixed-size fields only)

A foreign key takes its target's identity type. See relations.

Enums#

enum Status { Active, Suspended, Closed }
Account { id: +uuid  status: ^Status }

PascalCase name + variants; 1-byte discriminant; serialized as the variant name; filterable / sortable (declaration order) / indexable. See enums.

Directives#

Enforced: @min @max @length @email @url @pattern/@regex (→ 422), @utf8 (inline string(N) only — without it a non-ASCII value is 422), @on_delete(restrict|cascade|set_null) (→ 409 / cascade / null).

On an inline string(N) the width is the length bound, so @max and the upper-bound spellings of @length are schema errors there; @min/@length(min:) still apply.

Semantic-only markers: @default @computed @fulltext @materialized @relations(*|fields) (component-only).

Model-level: @index(a, b, ...) (composite, ≥ 2 fields), @projection(name: cols), @soft_delete.

Args accept numbers, bare idents, and quoted strings. See directives and indexes & projections.

Comments#

// line comments only. No block comments (/* */). Newlines are significant; no semicolons. See comments & whitespace.

Not supported#

~ auto-update · text type · block comments /* */.

Kitchen-sink schema#

// Every construct in one file.
enum OrderStatus { Placed, Paid, Shipped, Delivered, Cancelled }
 
struct GeoPoint {
  latitude: f64
  longitude: f64
}
 
User {
  id: +uuid
  email: ^&string @email
  name: string @length(1, 100)
  bio: string?
  home: GeoPoint?
  balance: decimal
  metadata: json?
  created_at: +timestamp
  orders: [Order]
  tags: [Tag]
}
 
Order {
  id: +uuid
  status: ^OrderStatus
  reference: &string(12!)          // fixed-width inline string, exactly 12 chars
  currency: ^string(3!)
  total: decimal @min(0)
  placed_at: +timestamp
  customer: *User @on_delete(cascade)
  courier: ?User @on_delete(set_null)
  scores: [u32; 5]
  @index(customer, placed_at)
  @projection(summary: status, total, placed_at)
  @soft_delete
}
 
Tag {
  id: +uuid
  label: ^&string @length(1, 50)
  users: [User]
}

Search documentation

Find pages across the ForgeDB docs