Skip to content
NEW

Introducing SurrealDB Studio, the new official app of SurrealDB

Learn more

1/5

Course content preview

Schema internals and migrations

Welcome to Schema internals and migrations, a SurrealDB University course on how schema lives inside the database, how DEFINE statements shape it, and how to change the schema of a live database without unpleasant surprises.

SurrealDB's philosophy on defined schema is that you can have as little or as much of it as you like. With schemaless as the default, you can run a full database without ever touching a DEFINE statement if that is what you want. However, production systems usually end up wanting typed fields, indexes, permissions, and a plan for evolving all of that safely.

This course covers what the database stores, how to tighten or loosen constraints, and how to migrate data without taking downtime you did not mean to take.

By the end you should be able to:

  • Read INFO FOR TABLE / INFO FOR DB output, and know how that differs from INFO … STRUCTURE

  • Choose between schemaless and schemafull tables, and use FLEXIBLE and union types when full strictness gets in the way

  • Change field types on live data with backfills, union types, and gradual normalisation

  • Use field clauses (DEFAULT, VALUE, COMPUTED, ASSERT, REFERENCE) to keep logic close to the data and cut down on code elsewhere

  • Decide when hand-written SurrealQL migrations are enough, and when SurrealKit is worth adopting

  • Grow a project planning schema from one table to graph relations, COMPUTED roll-ups, indexes, and DEFINE EVENT, using SurrealKit sync, rollouts, and test along the way

PartTopicFocus
1Schemaless vs schemafullSchemaless defaults, STRICT, type flexibility, security notes
2Schema internalsStored form of DEFINE, INFO / STRUCTURE, SurrealDB Studio Designer
3MigrationsBackfills, sandbox testing, gradual widen → normalise → tighten, when to use SurrealKit
4Data typesLiterals, records, datetimes, regex, geometry (and geohashes), bytes, objects, arrays and sets
5AutomationClauses that assign or validate values, UPSERT / ON DUPLICATE KEY UPDATE, permissions beside the data, when to reach for DEFINE EVENT
6SurrealKit and the first tablesurrealkit init --minimal, database/schema/, first project table, sync
7Activities and seed dataactivity table, split schema files, sync --watch, database/seed/
8Computed and asserted fieldsCOMPUTED duration, ASSERT on progress, VALUE timestamps
9Graph dependenciesRELATION tables depends_on and activity_of, graph COMPUTED followed_by
10Sync vs rolloutsDisposable sync vs shared rollouts, manifests, expand → deploy → contract; park milestone.surql unapplied
11MilestonesPreview parked milestone with sync --dry-run, then apply once with a rollout
12People and indexesemployee, option<record<>>, REFERENCE, DEFINE INDEX / UNIQUE
13Events and CIDEFINE EVENT lifecycle rules, surrealkit test
14CapstoneAlign with the docs project planning sample, rollout baseline, environments


The latter part of this course builds a project planning schema, one of many sample schemas divided by industry that you can find in the documentation. While a modest size at about 25 lines in length, it nevertheless represents the core of the sort of schema you will see in production.

This schema was chosen because it is fairly general but contains a lot of interesting tidbits such as COMPUTED fields, assertions, graph links, and closure functions. You can see what the schema looks like below. In the latter half of this course we will be building it up over several files instead of one to demonstrate how SurrealKit is able to combine them all and perform syncs or rollouts every time a change is made.

-- Activities in a project schedule
DEFINE TABLE activity SCHEMAFULL;
DEFINE FIELD name         ON activity TYPE string;
DEFINE FIELD description  ON activity TYPE option<string>;
DEFINE FIELD start        ON activity TYPE datetime;
DEFINE FIELD end          ON activity TYPE datetime;
DEFINE FIELD duration     ON activity COMPUTED end - start;
DEFINE FIELD progress     ON activity TYPE float ASSERT $value IN 0.0..=1.0;
DEFINE FIELD assigned_to  ON activity TYPE option<record<employee>>;
DEFINE FIELD followed_by  ON activity COMPUTED <-depends_on<-activity;

-- Milestones
DEFINE TABLE milestone SCHEMAFULL;
DEFINE FIELD project      ON milestone TYPE record<project>;
DEFINE FIELD activities   ON milestone TYPE array<record<activity>>;
DEFINE FIELD name         ON milestone TYPE string;
DEFINE FIELD last_updated ON milestone VALUE time::now();
DEFINE FIELD progress     ON milestone COMPUTED math::mean(activities.progress);
DEFINE FIELD is_complete  ON milestone COMPUTED activities.all(|$a| $a.progress > 0.95);

-- Graph-style dependency links
DEFINE TABLE depends_on SCHEMAFULL TYPE RELATION IN activity OUT activity;
DEFINE TABLE activity_of SCHEMAFULL TYPE RELATION IN activity OUT project;


You should be comfortable running SurrealQL in the CLI or SurrealDB Studio, and with basic CREATE / SELECT / UPDATE. If any of that sounds unfamiliar, the Tour of SurrealDB and SurrealDB Fundamentals are good places to start first.

Get started

1: Schemaless vs. schemafull

SurrealDB

The context layer for AI agents.

Documents, graphs, vectors, time-series, and memory.
One transaction, one query, one deployment.

Explore with AI

Stay in the loop

Tutorials, AI agent recipes, and product updates, every two weeks.

Independently verified

SOC 2 Type 2

GDPR

Cyber Essentials Plus

ISO 27001

Trust Centre

Copyright © 2026 SurrealDB Ltd. Registered in England and Wales. Company no. 13615201

Registered address: 3rd Floor 1 Ashley Road, Altrincham, Cheshire, WA14 2DT, United Kingdom

Trading address: Huckletree Oxford Circus, 213 Oxford Street, London, W1D 2LG, United Kingdom