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 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
Resource Tiers
| Tier | CPU | Memory | Disk | Replicas | Best For |
|---|---|---|---|---|---|
| Small | 0.5 | 1GB | 10GB | 1 | Development, testing |
| Medium | 1 | 2GB | 25GB | 1 | Small production apps |
| Large | 2 | 4GB | 50GB | 1 | Production workloads |
| XLarge | 4 | 8GB | 100GB | 1 | High-traffic applications |
| Custom | User-defined | User-defined | User-defined | User-defined | Specific requirements |
SurrealDB add-ons run with 1 replica across all tiers. Scale vertically by choosing a larger tier for more demanding workloads.
Creating a SurrealDB Add-on
- Navigate to Add-ons -> Create Add-on
- Select SurrealDB as the type
- Choose a version (2.1, 2.0, or 1.5)
- Configure:
- Label: Descriptive name (e.g., "App Database")
- Description: Purpose and notes
- Resource Tier: Based on your workload requirements
- Configure backups:
- Schedule: Hourly, Daily, Weekly, or Monthly
- Retention: Number of backups to keep (default: 7)
- Click Create Add-on
Connection Information
After deployment, connection details are available in the add-on details page and automatically injected into your apps via STRONGLY_SERVICES.
Connection Format
SurrealDB uses HTTP/WebSocket for client connections on port 8000:
http://host:8000
ws://host:8000/rpc
Credentials (username and password) 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 alphanumeric string.
Accessing Connection Details
- Python
- Node.js
- Go
import os
import json
from surrealdb import Surreal
# Parse STRONGLY_SERVICES
services = json.loads(os.environ.get('STRONGLY_SERVICES', '{}'))
# Get SurrealDB add-on connection
surreal_addon = services['addons']['addon-id']
# Connect using host and port
async def main():
db = Surreal(f"ws://{surreal_addon['host']}:{surreal_addon['port']}/rpc")
await db.connect()
# Sign in
await db.signin({
"user": surreal_addon['username'],
"pass": surreal_addon['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.addons['addon-id'];
// Connect
const db = new Surreal();
await db.connect(`ws://${surrealAddon.host}:${surrealAddon.port}/rpc`);
// Sign in
await db.signin({
username: surrealAddon.username,
password: surrealAddon.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 Services struct {
Addons map[string]Addon `json:"addons"`
}
type Addon struct {
Host string `json:"host"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
}
func main() {
var services Services
json.Unmarshal([]byte(os.Getenv("STRONGLY_SERVICES")), &services)
surrealAddon := services.Addons["addon-id"]
// Connect
db, err := surrealdb.New(
fmt.Sprintf("ws://%s:%d/rpc", surrealAddon.Host, surrealAddon.Port),
)
if err != nil {
panic(err)
}
defer db.Close()
// Sign in
_, err = db.Signin(map[string]interface{}{
"user": surrealAddon.Username,
"pass": surrealAddon.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
- Format: Database export
- Includes: Schema, data, relationships
- Storage: AWS S3 (
s3://strongly-backups/backups/<addon-id>/)
Manual Backup
- Go to add-on details page
- Click Backup Now
- Monitor progress in job logs
Scheduled Backups
Configure during add-on creation or in settings:
- Hourly: For critical data with frequent changes
- Daily: Recommended for most production workloads
- Weekly/Monthly: For less frequently changing data
- Retention: 7-14 days minimum for production
Backup restoration replaces ALL current data. Create a current backup before restoring if needed.
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
Monitor your SurrealDB add-on through the Strongly platform:
- CPU Usage: Track CPU utilization
- Memory Usage: Monitor memory consumption
- Disk Space: Watch disk utilization
- Connection Count: Active WebSocket connections
- Query Performance: Slow query detection
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
- Resource Planning: Start with appropriate tier, scale up as needed
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 Strongly dashboard
- Review SurrealDB official documentation
- Contact Strongly support through the platform