Skip to main content

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

TierCPUMemoryDiskReplicasBest For
Small0.51GB10GB1Development, testing
Medium12GB25GB1Small production apps
Large24GB50GB1Production workloads
XLarge48GB100GB1High-traffic applications
CustomUser-definedUser-definedUser-definedUser-definedSpecific requirements
SurrealDB Replicas

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

  1. Navigate to Add-ons -> Create Add-on
  2. Select SurrealDB as the type
  3. Choose a version (2.1, 2.0, or 1.5)
  4. Configure:
    • Label: Descriptive name (e.g., "App Database")
    • Description: Purpose and notes
    • Resource Tier: Based on your workload requirements
  5. Configure backups:
    • Schedule: Hourly, Daily, Weekly, or Monthly
    • Retention: Number of backups to keep (default: 7)
  6. 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

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())

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

Backup & Restore

Backup Configuration

  • Format: Database export
  • Includes: Schema, data, relationships
  • Storage: AWS S3 (s3://strongly-backups/backups/<addon-id>/)

Manual Backup

  1. Go to add-on details page
  2. Click Backup Now
  3. 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
Data Loss

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

  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
  10. 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