Cerial
Field Types

Any

Any field type in Cerial — CerialAny recursive union type for storing any SurrealDB value with type safety.

A pass-through type that stores any SurrealDB value. Unlike bare TypeScript any, the Any field type uses CerialAny — a recursive union that preserves type safety by enumerating all possible value types.

Schema Syntax

model Flexible {
  id Record @id
  data Any
  items Any[]
}

Types

DirectionType
OutputCerialAny
InputCerialAny
SurrealDBany

CerialAny is a recursive union covering all SurrealDB value types:

type CerialAny =
  | string
  | number
  | boolean
  | Date
  | CerialId
  | CerialUuid
  | CerialDuration
  | CerialDecimal
  | CerialBytes
  | CerialGeometry
  | null
  | CerialAny[]
  | { [key: string]: CerialAny };

Not bare TypeScript any

CerialAny is a typed recursive union, not TypeScript's any. You get full IntelliSense and type checking — the compiler knows exactly which types are possible. If you pass a value that isn't in the union (e.g., a class instance), TypeScript will flag it.

Modifiers

ModifierAllowed
[]Yes — array of any values
?No
@nullableNo

SurrealDB's TYPE any natively accepts both NONE (absent) and null values, so CerialAny already includes null in its union. Adding ? or @nullable would be redundant and is not allowed.

Create & Update

const flex = await client.db.Flexible.create({
  data: {
    data: { key: 'value', nested: [1, 2, 3] },
    items: ['hello', 42, true, null],
  },
});

// Any field accepts any CerialAny-compatible value
await client.db.Flexible.updateUnique({
  where: { id: flex.id },
  data: {
    data: 'now a string',
  },
});

// Array operations
await client.db.Flexible.updateUnique({
  where: { id: flex.id },
  data: {
    items: { push: { nested: 'object' } },
  },
});

The data transformer and result mapper pass Any values through without conversion. What you put in is what you get out — no wrapping or unwrapping.

Filtering

Any fields support the full operator set. SurrealDB handles type mismatches gracefully at query time — for example, a gt filter on a string value simply returns no matches.

// Equality
await client.db.Flexible.findMany({
  where: { data: { eq: 'hello' } },
});

// Numeric comparison
await client.db.Flexible.findMany({
  where: { data: { gt: 10, lte: 100 } },
});

// String operators
await client.db.Flexible.findMany({
  where: { data: { contains: 'sub' } },
});

// Set operators
await client.db.Flexible.findMany({
  where: { data: { in: [1, 2, 3] } },
});

// Range
await client.db.Flexible.findMany({
  where: { data: { between: [0, 100] } },
});

Available Operators

OperatorDescription
eqEqual to
neqNot equal to
gtGreater than
gteGreater than or equal
ltLess than
lteLess than or equal
betweenWithin a range (inclusive)
containsString contains substring
startsWithString starts with prefix
endsWithString ends with suffix
inIn a set of values
notInNot in a set of values

Since Any fields can't be optional or nullable, the conditional operators (not, isNone, isNull) are not generated. The isDefined operator is still available.

OrderBy

Any fields are excluded from orderBy — mixed value types make ordering ambiguous.

In Objects

Any works inside embedded object types:

object FlexInfo {
  data Any
  label String
}

model Config {
  id Record @id
  info FlexInfo
}

Limitations

Not allowed in tuples

Any is not supported as a tuple element type. SurrealDB cannot express any within typed tuple element definitions.

  • No ? or @nullableCerialAny already includes null, and SurrealDB TYPE any natively accepts NONE.
  • No orderBy — Mixed types make ordering ambiguous.
  • Not in tuples — SurrealDB can't express any in typed tuple literals.
  • Pass-through transform — Values go in and come out without conversion.

Supported Decorators

DecoratorEffect
@default(value)Default value on create
@defaultAlways(value)Reset to value on every write
@readonlyWrite-once field
@indexDatabase index
@uniqueUnique constraint
@sortArray only — auto-sort elements
@distinctArray only — remove duplicates

On this page