Skip to content

Overview

Architecture

SurrealDB has one query engine that runs on several storage engines. The query engine speaks one query language and exposes one API, and the storage engine decides how data is persisted, replicated and scaled. The same database, queries and SDK calls therefore work from an embedded edge application through to a distributed cluster.

This page describes the query layer and the storage engines, how the data models share a single store, and how a SurrealDB deployment is organised.

The query layer handles client requests and coordinates work against storage:

  • Parses and executes SurrealQL

  • Authenticates connections and sessions, for example through SIGNIN and access methods

  • Enforces table- and field-level PERMISSIONS as records are read and written

  • Plans index-backed queries, updates index entries on writes, and coordinates transactions against storage

Incoming SurrealQL passes through a parser, an executor that groups statements into transactions, an iterator that plans data access and fetches keys from storage, and a document processor that applies permissions and persists changes through the storage API.

Every transaction runs under snapshot isolation with write conflict detection on commit, whichever storage backend sits underneath. On SurrealMX and SurrealDS, a transaction can ask for serializable isolation instead. A SELECT ... FOR UPDATE read extends that detection to records the transaction reads without writing.

The storage layer handles persistence and durability. It determines deployment characteristics such as scalability, temporal versioning, replication and fault tolerance. SurrealDB integrates with several engines depending on how you run the database:

ConcernTypical engine
Single-node productionSurrealKV
Embedded on diskSurrealKV
In-memory server or embeddedSurrealMX
Browser persistenceSurrealKV over the browser's Origin Private File System
Distributed multi-nodeSurrealDS in self-hosted Enterprise, and distributed storage on SurrealDB Cloud Scale

SurrealDB 4.0 uses SurrealKV 1.0, which has a stable on-disk format and converts an existing RocksDB data directory the first time it opens it. To convert ahead of time, before starting the new server, the skv migrate tool writes the converted data to a new directory and keeps the original. See Moving a RocksDB database to SurrealKV.

Each engine must support transactional read and write of individual keys and key ranges. That is the whole contract, which is why the query layer can offer identical semantics across every deployment model.

SurrealDB is a document database at its core. Each record is a document held on a key-value engine, and it can carry arbitrary nested objects and arrays.

The other models come from how those keys are laid out rather than from separate subsystems:

  • Record IDs sort in a defined order, so a range read over a table returns records in that order. This is what makes time-series access a range scan rather than a scan-and-filter.

  • Graph edges are records of their own, created with RELATE. Traversal reads the edge table by key, so a graph query stays a series of key lookups.

  • Indexes for vector and full-text search are index entries in the same store, maintained by the query layer as records change.

One consequence matters for application design: a write that touches a document, its edges and its index entries is one transaction against one store, so those parts cannot drift apart.

SurrealDB is a multi-tenant platform. Resources nest in four levels, and each level has its own DEFINE statement:

LevelStatementPurpose
NamespaceDEFINE NAMESPACEIsolation for an organisation, department or team. No limit on the number of namespaces.
DatabaseDEFINE DATABASEThe unit that holds data. Each database has its own tables, indexes, schema, settings and permissions. No limit per namespace.
TableDEFINE TABLEA collection of records. Called a collection in some other systems.
FieldDEFINE FIELDA typed field on a table, with optional assertions and permissions.

A row or document in SurrealDB is called a record, and a column is called a field. Records are created, read, updated and deleted, with further statements for common patterns: UPSERT writes a record whether or not it already exists, and RELATE links two records through an edge table.

A namespace is the outer container. It holds databases and nothing else, which makes it the natural boundary in multi-tenant deployments where separate applications or teams share one instance or cluster. Permissions and access methods can be granted at namespace level rather than per database.

A database sits inside a namespace and is where data lives: tables, records, indexes, events, functions and access methods. Most work happens here.

Both are defined with a unique name and an optional comment. The USE statement switches the session from one namespace or database to another.

DEFINE NAMESPACE dev_namespace COMMENT "Internal use only: do not use in prod";
USE NAMESPACE dev_namespace;
-- Now inside 'dev_namespace', define a database within it
DEFINE DATABASE dev_db_1 COMMENT "First of many dev databases";

Multiple access methods can be defined on a namespace or a database. The record access method authenticates your end users against your own tables, down to field level.

The INFO statement reports what exists at each level:

  • INFO FOR ROOT: namespaces, root users, and system information such as allocated memory and physical cores.

  • INFO FOR NAMESPACE: the namespace's databases, users and access methods.

  • INFO FOR DATABASE: the database's tables, users, accesses, functions, analysers and more.

Each resource is reported as the DEFINE statement that would recreate it, which makes the output a readable snapshot of the schema:

Abridged output of INFO FOR DATABASE
{
	accesses: {  },
	analyzers: {
		blank_snowball: 'DEFINE ANALYZER blank_snowball TOKENIZERS BLANK FILTERS LOWERCASE, SNOWBALL(ENGLISH)'
	},
	functions: {
		pound_to_usd: 'DEFINE FUNCTION fn::pound_to_usd($price: number) -> float { $price * 1.26f } PERMISSIONS FULL'
	},
	tables: {
		order: 'DEFINE TABLE order TYPE RELATION IN person OUT product SCHEMAFULL PERMISSIONS NONE',
		user: 'DEFINE TABLE user TYPE ANY SCHEMALESS PERMISSIONS NONE'
	},
	users: {
		Boris: "DEFINE USER Boris ON DATABASE PASSHASH '[REDACTED]' ROLES VIEWER DURATION FOR TOKEN 1h, FOR SESSION NONE"
	}
}

Further INFO statements report the state of individual tables, users and indexes.

The same database runs in four ways:

  • Embedded, in memory through SurrealMX, or on disk through SurrealKV, including in the browser over the Origin Private File System.

  • Single-node self-hosted, on SurrealKV.

  • Multi-node self-hosted, with SurrealDS in SurrealDB Enterprise, on Kubernetes or on your own machines.

  • Managed, through managed instances, from single-node Start instances to multi-node Scale clusters on distributed storage.

Development can begin on an embedded or single-node deployment and move to a cluster later without changing application code or queries.

For storage trade-offs and how to choose a model, see Deployment models.

SurrealDB works as a conventional database behind a backend service, using the SDKs for Go, Python, Rust, C, Java, .NET, Node.js or PHP.

It can also serve a frontend directly. Table-, record- and field-level permissions, combined with record access authentication, let a browser or mobile client connect to the database and still only see the data it is entitled to. The JavaScript SDK, WebAssembly and the framework integrations for React, Next.js, Vue, Svelte and others support this arrangement.

Both approaches use the same query language and the same permission model, so a project can start with one and add the other later.

Was this page helpful?