Cerial
Decorators

@nullable

Mark a field as accepting explicit null values — separate from NONE (absent).

Marks a field as accepting an explicit null value. Without @nullable, fields can only hold a typed value or NONE (absent) — never null.

Syntax

field Type @nullable       // required but nullable: value or null
field Type? @nullable      // optional and nullable: value, null, or NONE

The Three-State Model

Cerial cleanly separates SurrealDB's three field states:

SchemaTypeScript OutputAllowed Values
name Stringname: stringvalue
bio String?bio?: stringvalue or NONE
bio String @nullablebio: string | nullvalue or null
bio String? @nullablebio?: string | nullvalue, null, or NONE
  • ? (optional) = the field can be absent (NONE). In TypeScript, this maps to undefined.
  • @nullable = the field can hold an explicit null value. In TypeScript, this maps to | null.
  • Both = the field can be a value, null, or absent.

See Optional Fields for full details on NONE vs null semantics.

Basic Usage

model User {
  id Record @id
  name String                 // required, never null
  bio String? @nullable       // optional and nullable
  deletedAt Date @nullable    // required but nullable (soft delete)
}
// deletedAt is required — always present in output, can be null
const user = await client.db.User.create({
  data: { name: 'Alice', deletedAt: null },
});
// user.deletedAt: Date | null → null

// bio is optional and nullable — can be omitted, null, or a string
const user2 = await client.db.User.create({
  data: { name: 'Bob' },
});
// user2.bio: string | null | undefined → undefined (NONE)

On Record Fields

@nullable is commonly used on optional Record fields (foreign keys) to distinguish between "no relation" (null) and "field not set yet" (NONE):

model Post {
  id Record @id
  title String
  authorId Record? @nullable
  author Relation? @field(authorId) @model(User)
}
// Create without author — authorId is NONE (absent)
const draft = await client.db.Post.create({ data: { title: 'Draft' } });

// Create with explicit null — authorId is null (unassigned)
const orphan = await client.db.Post.create({
  data: { title: 'Orphan', authorId: null },
});

// Query by null (find unassigned posts)
const unassigned = await client.db.Post.findMany({
  where: { authorId: { isNull: true } },
});

// Query by NONE (find posts without authorId set at all)
const noField = await client.db.Post.findMany({
  where: { authorId: { isNone: true } },
});

Disconnect Behavior

The @nullable decorator affects how disconnect works on relations:

SchemaDisconnect sets FK toRationale
Record? (no @nullable)NONE (field removed)Field can only be value or absent
Record? @nullableNULL (field set to null)Field supports null, so null is preferred

Default @onDelete Behavior

@nullable also affects the default @onDelete action for optional relations:

SchemaDefault @onDeleteDescription
Record? (no @nullable)SetNoneFK removed (field absent)
Record? @nullableSetNullFK set to null

You can always override with an explicit @onDelete(Action).

Where Operators

The available null/none operators depend on the field's modifiers:

SchemaisNullisNoneisDefined
String (required)NoNoNo
String? (optional only)NoYesYes
String @nullable (nullable only)YesNoNo
String? @nullable (both)YesYesYes

On Tuple Elements

@nullable is allowed on individual tuple elements. In fact, it is the only way to make a tuple element nullable — the ? modifier is not allowed on tuple elements because SurrealDB returns null (not NONE) for absent tuple positions.

tuple Coordinate {
  x Float,
  y Float,
  z Float @nullable        // element can be value or null
}
// Create with null element
await client.db.Model.create({
  data: { coord: [1.0, 2.0, null] },
});

Do not use ? on tuple elements — it is not supported. Use @nullable instead to allow null values at specific positions.

Restrictions

@nullable is not allowed on:

  • Object fields — SurrealDB cannot define sub-fields on a nullable object parent (object | null). Use ? for optional objects instead.
  • Tuple fields — Same limitation as objects. Use ? for optional tuples.
  • Any fieldsCerialAny already includes null in its union type, making @nullable redundant.
  • @id fields — Record IDs are always present and cannot be null.
  • @now fields — Computed fields have no stored value to be null.
model Example {
  id Record @id
  address Address?            // ✅ Optional object (can be absent)
  address Address @nullable   // ❌ Error — not allowed on object fields
  coord Coordinate?           // ✅ Optional tuple (can be absent)
  coord Coordinate @nullable  // ❌ Error — not allowed on tuple fields
}

Allowed On

ConstructAllowed
Model fields
Object fields✅ (on sub-fields, not on the object field itself)
Tuple elements✅ (the only way to make elements nullable)
Enum values
Literal fields

Mutual Exclusions

@nullable is compatible with most decorators. It is incompatible with:

  • @now — computed fields have no stored value to be null
  • @id — the primary record identifier cannot be null

With @default

@default(null) requires @nullable on the field. Without it, a null default makes no sense since the field can't hold null:

model User {
  id Record @id
  bio String? @nullable @default(null)    // ✅ Valid — omit → null stored
}

On this page