Skip to main content

SurrealDB

SurrealDB

SurrealDB is a multi-model database for modern applications, combining document, graph, and relational capabilities in a single platform.

Overview​

  • Versions: 2.1, 2.0, 1.5 (default: 2.1)
  • Default Port: 8000
  • Cluster Support: No (Single node only)
  • Use Cases: Multi-model data, modern applications, real-time APIs
  • Features: Document store, graph queries, relational joins, real-time subscriptions

Key Features​

  • Multi-Model: Document, graph, and relational models in one database
  • SurrealQL: Powerful query language combining SQL-like syntax with graph traversals
  • Real-Time Subscriptions: Live queries for real-time data updates
  • Schema Flexibility: Schemaless or schemafull modes
  • Built-in Auth: Row-level security and authentication
  • ACID Transactions: Full transaction support
  • Record Links: Native graph-style relationships between records
  • Computed Fields: Define fields that auto-compute from other data
  • Events and Triggers: React to data changes automatically

Resources​

Choose the add-on's resources on the create form:

SettingOptionsDefault
CPU (vCPU)Any number of cores, e.g. 0.5, 1, 20.5
MemoryAny amount in GB, at least the type's minimum1 GB
DiskAny amount in GB10 GB
GPU Count0-8 (0 for CPU-only)0

Creating a SurrealDB Add-on​

  1. Navigate to Add-ons and click Create Add-on
  2. On the Create New Add-on page, select SurrealDB as the type
  3. Choose a version (2.1, 2.0, or 1.5)
  4. Configure:
    • Add-on Label (required): descriptive name (e.g., "app-database")
    • Description (optional): purpose and notes
    • Resources: CPU, memory, and disk for your workload
  5. Optionally enable automatic backups:
    • Schedule: Hourly, Daily, Weekly, or Monthly
    • Retention: number of backups to keep (1-30, default 7)
  6. Click Create Add-on

Connection Information​

Once the add-on is running, the Connection tab of the add-on details page shows the internal host (for apps), port, username, and password. The same details are exposed to your apps via STRONGLY_SERVICES, where the connection string uses the WebSocket RPC endpoint:

ws://username:password@host:8000/rpc

Credentials are auto-generated during add-on creation. The username is a randomly generated string (e.g., user_a1b2c3d4), and the password is a 32-character random secret.

Accessing Connection Details​

In STRONGLY_SERVICES, add-ons are grouped by type under services.addons, and each entry is one provisioned instance:

{
"id": "addon-abc123defg",
"name": "app-database",
"type": "surrealdb",
"category": "add-on",
"status": "running",
"version": "2.1",
"connection": {
"connection_string": "ws://user_a1b2c3d4:<password>@<internal-host>:8000/rpc",
"uri": "ws://user_a1b2c3d4:<password>@<internal-host>:8000/rpc",
"host": "<internal-host>",
"port": 8000
},
"auth": {
"method": "username_password",
"credentials": { "username": "user_a1b2c3d4", "password": "<password>" }
},
"limits": { "max_connections": 100, "storage_gb": 10 },
"metadata": { "cpu": "0.5", "memory": "1GB", "disk": "10GB", "backup_enabled": false }
}
import os
import json
from surrealdb import Surreal

# Parse STRONGLY_SERVICES
services = json.loads(os.environ['STRONGLY_SERVICES'])

# Pick your SurrealDB add-on by name (the label you gave it)
surreal_addon = next(
a for a in services['services']['addons']['surrealdb']
if a['name'] == 'app-database'
)

conn = surreal_addon['connection']
auth = surreal_addon['auth']['credentials']

# Connect using host and port
async def main():
db = Surreal(f"ws://{conn['host']}:{conn['port']}/rpc")
await db.connect()

# Sign in
await db.signin({
"user": auth['username'],
"pass": auth['password']
})

# Select namespace and database
await db.use("test", "test")

# Create a record
await db.create("person", {
"name": "Alice",
"age": 30,
"active": True
})

# Query records
result = await db.query("SELECT * FROM person WHERE active = true")
print(result)

await db.close()

import asyncio
asyncio.run(main())

SurrealQL Query Language​

SurrealQL combines SQL-like syntax with graph traversals and modern features.

Basic CRUD Operations​

-- Create a record with auto-generated ID
CREATE person SET name = 'Alice', age = 30, active = true;

-- Create with specific ID
CREATE person:alice SET name = 'Alice', age = 30, active = true;

-- Select all records
SELECT * FROM person;

-- Select with conditions
SELECT * FROM person WHERE age > 25 AND active = true;

-- Update a record
UPDATE person:alice SET age = 31, updated_at = time::now();

-- Update with merge
UPDATE person:alice MERGE { email: 'alice@example.com' };

-- Delete a record
DELETE person:alice;

-- Delete with condition
DELETE person WHERE active = false;

SurrealDB supports native graph-style relationships:

-- Create records with relationships
CREATE person:alice SET name = 'Alice';
CREATE person:bob SET name = 'Bob';
CREATE company:acme SET name = 'Acme Corp';

-- Create a relationship
RELATE person:alice->works_at->company:acme
SET since = '2020-01-01', role = 'Engineer';

RELATE person:alice->knows->person:bob
SET since = '2019-06-15';

-- Traverse relationships
SELECT ->works_at->company.name FROM person:alice;

-- Reverse traversal
SELECT <-works_at<-person.name FROM company:acme;

-- Multi-hop traversal
SELECT ->knows->person->works_at->company.name FROM person:alice;

-- Query relationships with conditions
SELECT ->works_at WHERE since > '2019-01-01' FROM person;

Subqueries and Advanced Queries​

-- Subquery
SELECT *, (SELECT count() FROM ->works_at GROUP ALL) AS job_count
FROM person;

-- Aggregation
SELECT
count() AS total,
math::mean(age) AS avg_age,
math::min(age) AS youngest,
math::max(age) AS oldest
FROM person
GROUP ALL;

-- Group by
SELECT
active,
count() AS total,
math::mean(age) AS avg_age
FROM person
GROUP BY active;

-- Order and limit
SELECT * FROM person
ORDER BY age DESC
LIMIT 10
START 20;

-- FETCH to resolve links
SELECT * FROM person FETCH works_at;

Computed Fields and Events​

-- Define a table with computed fields
DEFINE TABLE person SCHEMAFULL;
DEFINE FIELD name ON person TYPE string;
DEFINE FIELD first_name ON person TYPE string;
DEFINE FIELD last_name ON person TYPE string;
DEFINE FIELD full_name ON person VALUE string::concat($value.first_name, ' ', $value.last_name);

-- Define an event
DEFINE EVENT person_created ON TABLE person WHEN $event = "CREATE" THEN (
CREATE audit SET
table = 'person',
action = 'create',
record = $after.id,
timestamp = time::now()
);

Permissions and Auth​

-- Define a scope for authentication
DEFINE SCOPE user SESSION 24h
SIGNUP (CREATE user SET email = $email, password = crypto::argon2::generate($password))
SIGNIN (SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(password, $password));

-- Define permissions on tables
DEFINE TABLE post SCHEMALESS
PERMISSIONS
FOR select WHERE published = true OR author = $auth.id
FOR create WHERE $auth.id IS NOT NONE
FOR update WHERE author = $auth.id
FOR delete WHERE author = $auth.id;

Common Use Cases​

Document Store​

-- Store flexible documents
CREATE article SET
title = 'Getting Started with SurrealDB',
content = 'SurrealDB is a multi-model database...',
tags = ['database', 'tutorial', 'surrealdb'],
metadata = {
author: 'Alice',
published: true,
views: 0,
created_at: time::now()
};

-- Query with nested fields
SELECT * FROM article WHERE metadata.published = true;

-- Full-text search
SELECT * FROM article WHERE title @@ 'SurrealDB';

Graph Database​

-- Social network
CREATE user:alice SET name = 'Alice';
CREATE user:bob SET name = 'Bob';
CREATE user:charlie SET name = 'Charlie';

RELATE user:alice->follows->user:bob;
RELATE user:bob->follows->user:charlie;
RELATE user:charlie->follows->user:alice;

-- Find mutual follows (friends)
SELECT id, name,
->follows->user AS following,
<-follows<-user AS followers
FROM user;

-- Friend recommendations (friends of friends)
SELECT ->follows->user->follows->user AS suggestions
FROM user:alice
WHERE suggestions != user:alice;

Real-Time Applications​

-- Live query (WebSocket)
LIVE SELECT * FROM messages WHERE channel = 'general';

-- The client receives real-time updates when:
-- - New messages are created
-- - Existing messages are updated
-- - Messages are deleted

Backups​

A SurrealDB backup is an export of each namespace and database (.surql files).

  • Back up now: click Backup Now on the status card, or Back Up Now on the Backup tab, while the add-on is running.

  • Automatic: on the Backup tab turn on Enable Automatic Backups, choose a Backup Schedule (Hourly, Daily, Weekly or Monthly) and a Retention (3, 7, 14 or 30 backups), and click Save Configuration. Older backups beyond the retention count are deleted automatically.

  • History: the Backup tab lists every backup with its status, size and any error.

  • Restore: click Restore next to a succeeded backup in Backup History and confirm. The backup is loaded back into this add-on while it keeps running: every database is removed and reloaded from the backup. Queries find missing data until the restore finishes. Data written after the backup is lost. See Restoring a backup.

Performance Optimization​

Indexing​

-- Create an index
DEFINE INDEX email_idx ON person FIELDS email UNIQUE;

-- Create a composite index
DEFINE INDEX name_age_idx ON person FIELDS name, age;

-- Create a search index for full-text search
DEFINE ANALYZER custom TOKENIZERS blank FILTERS lowercase, snowball(english);
DEFINE INDEX content_search ON article FIELDS content SEARCH ANALYZER custom;

Query Optimization​

-- Use specific record IDs when possible (fastest)
SELECT * FROM person:alice;

-- Use indexes for filtering
SELECT * FROM person WHERE email = 'alice@example.com';

-- Limit result sets
SELECT * FROM person LIMIT 100;

-- Use FETCH sparingly - only when you need linked data
SELECT * FROM person FETCH works_at;

Monitoring​

The Metrics tab on the add-on details page measures the running add-on live: CPU, memory and disk use against its size, network traffic, open and new connections, response time, and instance health and uptime. See Metrics. The Logs tab shows its recent log output.

Best Practices​

  1. Use Record Links: Leverage native graph relationships instead of foreign keys
  2. Define Schemas: Use SCHEMAFULL for production tables requiring data validation
  3. Index Strategically: Create indexes on frequently queried fields
  4. Use Namespaces: Organize data into logical namespaces and databases
  5. Leverage Live Queries: Use real-time subscriptions for reactive UIs
  6. Batch Operations: Insert multiple records in a single transaction
  7. Set Permissions: Define table-level permissions for security
  8. Backup Regularly: Enable daily backups for production databases
  9. Monitor Memory: Watch memory usage as data grows

Troubleshooting​

Connection Issues​

# Test connection via HTTP
import requests

response = requests.post(
f'http://{host}:8000/sql',
headers={
'Accept': 'application/json',
'NS': 'test',
'DB': 'test',
},
auth=(username, password),
data='INFO FOR DB;'
)
print(response.json())

Query Issues​

-- Check table info
INFO FOR TABLE person;

-- Check database info
INFO FOR DB;

-- Check namespace info
INFO FOR NS;

Support​

For issues or questions:

  • Check add-on logs in the Logs tab of the add-on details page
  • Review SurrealDB official documentation
  • Contact Strongly support through the platform