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:
namespace acme;
database shop;
/// Default page size for list endpoints.
param $default_limit = 50;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:
namespace acme;
database shop;
param $endpoints = { read: '/api/read', write: '/api/write' };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:
namespace acme;
database shop;
param $internal_seed {
value: 42;
permissions: none;
};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):
namespace acme;
database shop;
param $placeholder;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'):
namespace acme;
database shop;
sequence invoice_number = 1000;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):
namespace acme;
database shop;
sequence order_number {
start: 5000;
batch: 100;
timeout: 5s;
};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:
namespace acme;
database shop;
sequence row_ids;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:
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;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
- Config — the other global knobs.
- The linter — the valueless-param warning.