SurrealDB: One Database for Modern Applications
SurrealDB is shaking up the way developers think about data storage. It blends the flexibility of a document store, the relational power of SQL, and the real‑time capabilities of a graph database into a single, unified engine. In this post we’ll explore why that matters for modern apps, walk through a couple of hands‑on Python examples, and uncover some pro tips you can start using right away.
Unlike traditional stacks where you juggle a relational DB for transactions, a NoSQL store for flexibility, and perhaps a message broker for live updates, SurrealDB lets you do it all from one place. The result is fewer moving parts, simpler deployments, and a data model that can evolve alongside your product without costly migrations.
Core Concepts that Set SurrealDB Apart
Before we dive into code, let’s demystify three core ideas that make SurrealDB unique: multi‑model data, SQL‑like query language, and built‑in realtime subscriptions. Understanding these will help you decide where SurrealDB fits in your architecture.
Multi‑Model Data
SurrealDB stores data as records that can act as documents, rows, or graph nodes depending on how you query them. Each record lives in a namespace and a database, giving you logical isolation similar to schemas in PostgreSQL.
- Document‑style:
{ name: "Alice", age: 30, tags: ["dev","blogger"] } - Relational‑style:
SELECT * FROM user WHERE age > 25 - Graph‑style:
SELECT OUT FROM follows WHERE IN = $user
SQL‑Like Query Language
SurrealDB’s query language feels familiar to anyone who has used SQL, but it adds a few twists. You can embed JSON directly, use dot‑notation for nested fields, and even write MERGE statements that upsert complex structures in a single line.
Because the language is declarative, you get the safety of type checking and query planning while still enjoying the schema‑less freedom of NoSQL.
Realtime Subscriptions
One of the most compelling features is the ability to subscribe to changes on any query. When a record that matches the query is created, updated, or deleted, SurrealDB pushes a notification to the client instantly. No extra WebSocket server, no polling loops.
Pro tip: Use subscriptions for collaborative dashboards, live chat, or any UI that needs to reflect data changes within milliseconds.
Getting Started with SurrealDB in Python
First, install the official Python driver. It’s a single dependency and works with both synchronous and asynchronous code.
pip install surrealdb
Next, spin up a local SurrealDB instance. The quickest way is via Docker:
docker run --rm -p 8000:8000 surrealdb/surrealdb:latest start --log trace
With the server running, you can connect, authenticate, and start issuing queries. Below is a minimal synchronous example that creates a user, adds a friend relationship, and queries the graph.
Example 1: A Simple Social Graph
from surrealdb import Surreal
# Connect to the local instance
db = Surreal('http://localhost:8000/rpc')
# Authenticate as the built‑in root user (only for dev)
db.signin({
"user": "root",
"pass": "root"
})
# Select a namespace and database
db.use('test', 'social')
# Create two user records
alice = db.create('user', {
"name": "Alice",
"age": 28,
"tags": ["developer", "blogger"]
})
bob = db.create('user', {
"name": "Bob",
"age": 31,
"tags": ["designer"]
})
# Establish a "follows" edge from Alice to Bob
db.relate(alice.id, 'follows', bob.id, {
"since": "2023-01-15"
})
# Query Alice's outgoing follows edges
result = db.query("""
SELECT OUT as follows FROM follows WHERE IN = $alice
""", {"alice": alice.id})
print("Alice follows:", result[0]['follows'])
The create method automatically generates a unique ID for each record, and relate creates a directional edge. Notice how the query mixes SQL syntax with JSON‑style field access – that’s the sweet spot of SurrealDB.
Example 2: Real‑Time Stock Ticker
Imagine a dashboard that shows live stock prices. With SurrealDB you can store price updates as simple documents and push changes to any subscribed client.
import time
import threading
from surrealdb import Surreal
db = Surreal('ws://localhost:8000/rpc')
db.signin({"user": "root", "pass": "root"})
db.use('finance', 'ticker')
# Function to simulate price updates
def publish_prices():
symbols = ["AAPL", "GOOG", "TSLA"]
while True:
for sym in symbols:
db.create('price', {
"symbol": sym,
"price": round(100 + 50 * random.random(), 2),
"ts": time.time()
})
time.sleep(2)
# Start publishing in a background thread
threading.Thread(target=publish_prices, daemon=True).start()
# Subscribe to the latest price for each symbol
def handle_update(event):
print("🔔 Update:", event)
db.subscribe("""
SELECT * FROM price
ORDER BY ts DESC
LIMIT 1
GROUP BY symbol
""", handle_update)
# Keep the script alive
while True:
time.sleep(1)
The subscription query groups the most recent price per symbol, orders by timestamp, and pushes a fresh snapshot every time a new price record arrives. The handle_update callback receives a Python dictionary with the updated data, ready to be rendered on a UI.
Pro tip: When using subscriptions, always include an ORDER BY clause to guarantee deterministic results, especially in high‑throughput scenarios.
Real‑World Use Cases
SurrealDB’s blend of features shines in domains where data relationships are fluid and real‑time feedback is essential. Below are three scenarios where teams have reported measurable gains.
IoT Telemetry Aggregation
IoT devices generate streams of sensor readings that need to be correlated with device metadata and alerts. SurrealDB lets you store each reading as a lightweight document, link it to a device node, and instantly trigger alerts via subscriptions. Because the same engine handles both time‑series queries and relational joins, you avoid the overhead of syncing multiple databases.
Collaborative Editing Platforms
Applications like shared whiteboards or code editors require every user’s changes to be reflected instantly for all participants. By persisting each edit operation as a record and subscribing to the edit stream, you get a built‑in conflict‑resolution layer without needing a separate CRDT service.
Gaming Leaderboards & Matchmaking
Online games often need to query player stats, match players based on skill, and broadcast live score updates. SurrealDB can store player profiles as documents, model friendships or teams as edges, and push score changes through subscriptions, all while supporting complex ranking queries written in familiar SQL.
Advanced Patterns and Performance Tips
While SurrealDB works out‑of‑the‑box for many scenarios, production workloads benefit from a few best practices. Below we cover indexing, transaction handling, and scaling considerations.
Indexing for Speed
- Primary indexes are automatically created on the
idfield. - Use
DEFINE INDEXto add secondary indexes on frequently filtered fields, e.g.,DEFINE INDEX idx_age ON user (age) UNIQUE;. - Composite indexes (multiple fields) can accelerate complex filters like
WHERE age > 25 AND tags CONTAINS "dev".
Transactions Across Multiple Records
SurrealDB supports ACID transactions that span documents, edges, and even multiple namespaces. Wrap your operations in a BEGIN; … COMMIT; block to guarantee atomicity.
db.query("""
BEGIN;
CREATE user SET name = $name, balance = $balance;
CREATE transaction SET from = $userId, amount = $amount;
UPDATE user SET balance = balance - $amount WHERE id = $userId;
COMMIT;
""", {"name": "Charlie", "balance": 1000, "userId": "user:123", "amount": 150})
If any step fails, SurrealDB rolls back the entire transaction, preventing partial state.
Scaling Horizontally
For read‑heavy workloads, you can deploy SurrealDB in a clustered mode using the built‑in replication flag. Replicas handle read queries while the primary node processes writes. Remember to keep your subscription clients connected to the primary to receive live updates.
Security Best Practices
- Never use the default
rootcredentials in production; create scoped users withDEFINE USERand assign fine‑grained permissions. - Enable TLS for the HTTP and WebSocket endpoints.
- Leverage SurrealDB’s
ROLEsystem to restrict access to specific namespaces or tables.
Pro tip: Combine SurrealDB’s role‑based access control with JWT authentication on your API layer for a defense‑in‑depth strategy.
Integrating SurrealDB with Existing Stacks
Most teams already have a web framework, an ORM, or a microservice architecture in place. SurrealDB can slot in without a complete rewrite. Below are three integration patterns.
As a Primary Data Store
Replace your traditional relational DB entirely. Use SurrealDB’s SQL dialect to migrate existing queries, and rely on its multi‑model capabilities to simplify data structures that previously required separate services.
As a Caching Layer
Keep your relational DB for authoritative storage but use SurrealDB as a fast, in‑memory cache for high‑frequency reads. Sync changes via triggers or change‑data‑capture (CDC) pipelines, and benefit from real‑time subscriptions for cache invalidation.
Hybrid Approach with Event Sourcing
Store immutable events in an event log (e.g., Kafka) and project them into SurrealDB for queryable state. The graph nature of SurrealDB makes it ideal for reconstructing aggregates and visualizing event relationships.
Common Pitfalls and How to Avoid Them
Even the most powerful tools have learning curves. Here are three mistakes newcomers often make, plus ways to sidestep them.
Over‑Normalizing Early
Because SurrealDB can store nested JSON directly, many developers create excessive edge tables to mimic classic relational designs. Start with a denormalized document model, then introduce edges only when you need explicit graph traversals.
Neglecting Indexes on High‑Cardinality Fields
Queries that filter on fields like email or username can degrade quickly without indexes. Define them early, and monitor query plans using the EXPLAIN command.
Assuming Subscriptions Are Free
Every subscription consumes server resources. In high‑traffic apps, batch subscriptions or limit the fields you request to reduce payload size. Also, clean up idle subscriptions on the client side.
Tooling and Ecosystem
SurrealDB’s ecosystem is growing fast. The official CLI, web console, and language SDKs (Python, JavaScript, Rust, Go) make it easy to prototype and ship. Community plugins provide integrations with popular ORMs like Prisma and SQLAlchemy, allowing you to write type‑safe models that compile down to SurrealQL.
For monitoring, SurrealDB emits structured logs and metrics compatible with Prometheus. Pair it with Grafana dashboards to track query latency, subscription counts, and replication lag.
Future Roadmap (What’s Coming Next?)
The core team has outlined several exciting features slated for the next releases:
- Full‑text search with fuzzy matching and relevance scoring.
- Vector embeddings for AI‑driven similarity queries.
- Native GraphQL endpoint that translates GraphQL queries directly into SurrealQL.
- Edge‑level permissions to restrict traversal based on user roles.
These additions aim to solidify SurrealDB as a one‑stop shop for everything from traditional CRUD apps to cutting‑edge AI workloads.
Conclusion
SurrealDB offers a compelling blend of document, relational, and graph capabilities wrapped in a familiar SQL‑like language, all while delivering real‑time subscriptions out of the box. By consolidating multiple data paradigms into a single engine, it reduces operational overhead, accelerates development, and opens doors to innovative architectures.
Whether you’re building an IoT telemetry platform, a collaborative editor, or a high‑score gaming leaderboard, SurrealDB can serve as both the source of truth and the real‑time engine that powers your UI. Start experimenting with the Python examples above, adopt the pro tips, and you’ll quickly see how a single database can simplify the complex data needs of modern applications.