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:
| Setting | Options | Default |
|---|---|---|
| CPU (vCPU) | Free entry (e.g., 0.5, 1, 2) | 0.5 |
| Memory | 1GB, 2GB, 4GB, 8GB, 16GB | 1GB |
| Disk Space | 1GB, 5GB, 10GB, 20GB, 50GB, 100GB | 10GB |
| GPU Count | 0-8 (0 for CPU-only) | 0 |
Creating a SurrealDB Add-on
- Navigate to Add-ons and click Create Add-on
- On the Create New Add-on page, select SurrealDB as the type
- Choose a version (2.1, 2.0, or 1.5)
- Configure:
- Add-on Label (required): descriptive name (e.g., "app-database")
- Description (optional): purpose and notes
- Resources: CPU, memory, and disk for your workload
- Optionally enable automatic backups:
- Schedule: Hourly, Daily, Weekly, or Monthly
- Retention: number of backups to keep (1-30, default 7)
- 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 }
}
- Python
- Node.js
- Go
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())
const { Surreal } = require('surrealdb.js');
// Parse STRONGLY_SERVICES
const services = JSON.parse(process.env.STRONGLY_SERVICES);
const surrealAddon = services.services.addons.surrealdb
.find(a => a.name === 'app-database');
// Connect
const db = new Surreal();
await db.connect(`ws://${surrealAddon.connection.host}:${surrealAddon.connection.port}/rpc`);
// Sign in
await db.signin({
username: surrealAddon.auth.credentials.username,
password: surrealAddon.auth.credentials.password,
});
// Select namespace and database
await db.use({ namespace: 'test', database: 'test' });
// Create a record
const person = await db.create('person', {
name: 'Alice',
age: 30,
active: true,
});
console.log(person);
// Query records
const result = await db.query('SELECT * FROM person WHERE active = true');
console.log(result);
await db.close();
package main
import (
"encoding/json"
"fmt"
"os"
"github.com/surrealdb/surrealdb.go"
)
type Connection struct {
Host string `json:"host"`
Port int `json:"port"`
}
type Auth struct {
Credentials struct {
Username string `json:"username"`
Password string `json:"password"`
} `json:"credentials"`
}
type Addon struct {
Name string `json:"name"`
Connection Connection `json:"connection"`
Auth Auth `json:"auth"`
}
type Services struct {
Services struct {
Addons map[string][]Addon `json:"addons"`
} `json:"services"`
}
func main() {
var services Services
json.Unmarshal([]byte(os.Getenv("STRONGLY_SERVICES")), &services)
surrealAddon := services.Services.Addons["surrealdb"][0]
// Connect
db, err := surrealdb.New(
fmt.Sprintf("ws://%s:%d/rpc", surrealAddon.Connection.Host, surrealAddon.Connection.Port),
)
if err != nil {
panic(err)
}
defer db.Close()
// Sign in
_, err = db.Signin(map[string]interface{}{
"user": surrealAddon.Auth.Credentials.Username,
"pass": surrealAddon.Auth.Credentials.Password,
})
if err != nil {
panic(err)
}
// Select namespace and database
_, err = db.Use("test", "test")
if err != nil {
panic(err)
}
fmt.Println("Connected to SurrealDB")
}
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;
Record Links (Graph Relationships)
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
Backup & Restore
Backup Configuration
- Tool:
surreal export - Format:
.surql - Includes: Schema, data, relationships
- Storage: the platform's S3 backup storage
Manual Backup
- Go to the add-on details page
- Open the Backup tab and click Create Manual Backup (the add-on must be running)
- The Backup tab shows the last backup time when complete
Scheduled Backups
Configure during add-on creation or on the Backup tab:
- Hourly: For critical data with frequent changes
- Daily: Recommended for most production workloads
- Weekly/Monthly: For less frequently changing data
- Retention: number of backups to keep (3, 7, 14, or 30 on the Backup tab)
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 shows:
- CPU Usage: CPU utilization percentage
- Memory Usage: memory utilization percentage
- Disk Space: disk utilization percentage
- Network I/O: current throughput
- Request Stats: connections per minute and average response time
- Instance Health: instance count and uptime
Best Practices
- Use Record Links: Leverage native graph relationships instead of foreign keys
- Define Schemas: Use SCHEMAFULL for production tables requiring data validation
- Index Strategically: Create indexes on frequently queried fields
- Use Namespaces: Organize data into logical namespaces and databases
- Leverage Live Queries: Use real-time subscriptions for reactive UIs
- Batch Operations: Insert multiple records in a single transaction
- Set Permissions: Define table-level permissions for security
- Backup Regularly: Enable daily backups for production databases
- 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