Cerial

Model

Define database tables with the model keyword

Models are the primary building block of a Cerial schema. Each model maps directly to a SurrealDB table and defines the fields, types, decorators, and relations for that table. Every model becomes a fully typed accessor on the generated client.

Syntax

Declare a model with the model keyword followed by a name and a block of field declarations:

model User {
  id Record @id
  email Email @unique
  name String
  age Int?
  isActive Bool @default(true)
  createdAt Date @createdAt
}

Model names use PascalCase — e.g., User, BlogPost, OrderItem. The name determines both the generated TypeScript type and the SurrealDB table name.

The id Field

Every model must have an id Record @id field — the primary key mapping to SurrealDB's built-in record ID. By default, it produces string-based IDs. Narrow the type for stricter safety:

model Product {
  id Record(int) @id      # CerialId<number>
}

model Session {
  id Record(uuid) @id     # CerialId<CerialUuid>
}

See Record for typed IDs and how they affect foreign keys.

Field Declarations

Each line inside a model block declares a field with the format fieldName Type [decorators...]:

model BlogPost {
  id Record @id
  title String
  content String?
  viewCount Int @default(0)
  tags String[]
  createdAt Date @createdAt
  updatedAt Date @updatedAt
}

Cerial supports 15 built-in types plus user-defined types. See Field Types for the complete reference.

Optional and Nullable

Append ? to make a field optional (maps to undefined). Add @nullable to allow explicit null. These are independent:

model User {
  id Record @id
  name String                  # required — always present
  bio String?                  # optional — can be absent (NONE)
  nickname String @nullable    # nullable — can be null
}

See Optional Fields for the full NONE vs null distinction.

Arrays

Append [] to any type for an array field. See Arrays for @set and push/remove operations.

Decorators

Fields can have one or more decorators for defaults, constraints, timestamps, and more:

model User {
  id Record @id
  email Email @unique
  role String @default("viewer")
  createdAt Date @createdAt
  updatedAt Date @updatedAt
}

See Decorators for the full list of available decorators.

Relations

Models relate to other models using Record (foreign key) and Relation (virtual traversal) fields:

model User {
  id Record @id
  name String
  posts Relation[] @model(Post)
}

model Post {
  id Record @id
  title String
  authorId Record
  author Relation @field(authorId) @model(User)
}

The authorId field stores the foreign key. The author Relation field is virtual — it describes traversal without storing data. See Relations for 1

, 1
, N
patterns and nested operations.

Abstract Models

Mark a model as abstract to use it as a reusable field template. Abstract models produce no database table, TypeScript types, or client accessor:

abstract model Timestamped {
  createdAt Date @createdAt
  updatedAt Date @updatedAt
}

model Order extends Timestamped {
  id Record @id
  total Float
  status String @default("pending")
}

Concrete models can only extend abstract models. See Inheritance for extends, pick/omit syntax, and !!private fields.

SurrealDB Mapping

Each model maps to a SurrealDB table. Cerial generates migration statements:

CerialSurrealDB
model User { ... }DEFINE TABLE User SCHEMAFULL
name StringDEFINE FIELD name ON User TYPE string
age Int?DEFINE FIELD age ON User TYPE option<int>
tags String[]DEFINE FIELD tags ON User TYPE array<string>
email Email @uniqueDEFINE INDEX user_email_unique ON User FIELDS email UNIQUE

The model name is used as-is for the table name. Field names map directly. Relation fields are virtual and produce no DEFINE FIELD statement.

On this page