surformSurrealDB schemas, declared

Params & sequences

Params and sequences are the two small database-side values a schema often needs: named constants and counters.

Params: define-time constants

A param is a $-named value evaluated once, when the schema applies — the result is stored, not the expression:

applied to SurrealDB 3.3.0
schema.surdl
sdl
namespace acme;
database shop;

/// Default page size for list endpoints.
param $default_limit = 50;
surform build
sql
DEFINE NAMESPACE OVERWRITE acme;
USE NS acme;
DEFINE DATABASE OVERWRITE shop;
USE NS acme DB shop;
DEFINE PARAM OVERWRITE $default_limit VALUE 50 COMMENT 'Default page size for list endpoints.';

The value is any SurrealQL expression — including an object literal, since the { after = is just the value's first character:

applied to SurrealDB 3.3.0
schema.surdl
sdl
namespace acme;
database shop;

param $endpoints = { read: '/api/read', write: '/api/write' };
surform build
sql
DEFINE NAMESPACE OVERWRITE acme;
USE NS acme;
DEFINE DATABASE OVERWRITE shop;
USE NS acme DB shop;
DEFINE PARAM OVERWRITE $endpoints VALUE { read: '/api/read', write: '/api/write' };

The define-time-freeze has a flip side: an expression like time::now() evaluates once, at apply, and the result is what the database stores — so $t reads as the moment the schema went on, not the moment you query it. (Watch the braces, too: { time::now() } is a one-element set, not a call.) Re-applying the emitted DDL freezes a fresh timestamp each time.

The block form

Anything beyond the value — permissions — needs the block form:

applied to SurrealDB 3.3.0
schema.surdl
sdl
namespace acme;
database shop;

param $internal_seed {
  value: 42;
  permissions: none;
};
surform build
sql
DEFINE NAMESPACE OVERWRITE acme;
USE NS acme;
DEFINE DATABASE OVERWRITE shop;
USE NS acme DB shop;
DEFINE PARAM OVERWRITE $internal_seed VALUE 42 PERMISSIONS NONE;

A param with no value: at all is legal but almost never what you meant — the linter says so (SDL-L0008):

real Surform lint findings
this file lints
sdl
namespace acme;
database shop;

param $placeholder;
what Surform suggests
text
SDL-L0008 (spec/param.md)

  ! param `placeholder` in database `acme/shop` has no `value:` — SurrealDB stores VALUE NONE, almost certainly unintended
   ,-[4:1]
 3 | 
 4 | param $placeholder;
   : ^^^^^^^^^|^^^^^^^^^
   :          `-- no `value:`
   `----
  help: add a `value:` clause — a clause-less param stores VALUE NONE

Sequences: monotonic counters

A sequence hands out ever-increasing numbers — invoice numbers, order ids — distributed-safe and never rolled back, even in failed transactions. Clients consume it with sequence::nextval('name'):

applied to SurrealDB 3.3.0
schema.surdl
sdl
namespace acme;
database shop;

sequence invoice_number = 1000;
surform build
sql
DEFINE NAMESPACE OVERWRITE acme;
USE NS acme;
DEFINE DATABASE OVERWRITE shop;
USE NS acme DB shop;
DEFINE SEQUENCE OVERWRITE invoice_number START 1000;

The short form sets the start value. The block form tunes the rest: batch: (how many values each node reserves at a time — the distributed-safety margin) and timeout: (the per-query acquisition limit):

applied to SurrealDB 3.3.0
schema.surdl
sdl
namespace acme;
database shop;

sequence order_number {
  start: 5000;
  batch: 100;
  timeout: 5s;
};
surform build
sql
DEFINE NAMESPACE OVERWRITE acme;
USE NS acme;
DEFINE DATABASE OVERWRITE shop;
USE NS acme DB shop;
DEFINE SEQUENCE OVERWRITE order_number BATCH 100 START 5000 TIMEOUT 5s;

With defaults everywhere, the bare form is the whole declaration — and normalize returns it that short, folding the database's verbose BATCH 1000 START 0 away:

applied to SurrealDB 3.3.0
schema.surdl
sdl
namespace acme;
database shop;

sequence row_ids;
surform build
sql
DEFINE NAMESPACE OVERWRITE acme;
USE NS acme;
DEFINE DATABASE OVERWRITE shop;
USE NS acme DB shop;
DEFINE SEQUENCE OVERWRITE row_ids;

One rule to remember: sequence has no COMMENT clause in SurrealQL, so a /// doc comment above one is rejected — use a plain // line instead:

applied to SurrealDB 3.3.0
schema.surdl
sdl
namespace acme;
database shop;

// Invoice numbers start at 1000 — a plain comment, because sequence
// is one of the two statements (with config) that take no doc
// comment.
sequence invoice_number = 1000;
surform build
sql
DEFINE NAMESPACE OVERWRITE acme;
USE NS acme;
DEFINE DATABASE OVERWRITE shop;
USE NS acme DB shop;
DEFINE SEQUENCE OVERWRITE invoice_number START 1000;

Where next