SurrealDB LIVE SELECT: Real-Time Queries Over WebSocket
Learn how to use SurrealDB's LIVE SELECT over WebSocket for real-time updates, with a minimal example, operational limits, and verification steps.
09 May 2026, 01:03 UTC

The Problem: Polling Doesn't Scale
Applications that need immediate visibility into database changes often resort to polling, which wastes resources and adds latency. SurrealDB offers a native alternative: LIVE SELECT over a persistent WebSocket or RPC connection. The server pushes notifications for matching CREATE, UPDATE, and DELETE events as they happen. The feature is tied to the client session, so it works without a separate message broker, but it also means the subscription disappears when the connection drops.
How LIVE SELECT Works
You start a live query by sending a LIVE SELECT statement over an authenticated WebSocket/RPC session that has already selected a namespace and database. The server responds with a live-query identifier. From that point on, any write that satisfies the WHERE predicate triggers a notification sent over the same connection. The notification envelope includes an action field (typically CREATE, UPDATE, or DELETE) and the affected record content. Exact field names vary by protocol and SDK version, so treat the shapes below as illustrative, not guaranteed output.
The live query remains active until you explicitly terminate it with KILL using the returned identifier, or until the connection closes. Because the subscription is bound to the session, a reconnect requires re-issuing LIVE SELECT and reconciling any missed events.
Minimal Working Example
Assume a local SurrealDB instance (v2.x release line) running with surreal start file://data. Open two WebSocket clients (for example wscat or an official SDK), authenticate, and select the same namespace and database in both.
-- Session A: start the live query
LIVE SELECT * FROM app.user WHERE active = true;
-- The response includes a live query id, for example:
-- { "id": ..., "result": "live-query-uuid" }
Save that id. In Session B, perform writes:
-- Session B: create a matching record
CREATE app.user SET name = 'Ada', active = true;
-- Session A receives a notification shaped roughly like:
-- { "action": "CREATE", "result": { "id": "app.user:...", "name": "Ada", "active": true } }
UPDATE app.user:... SET name = 'Ada Lovelace';
-- Session A receives an UPDATE notification with the new content.
DELETE app.user:...;
-- Session A receives a DELETE notification.
To stop the stream in Session A, run KILL with the saved id:
KILL live-query-uuid;
After the KILL, further writes in Session B produce no notifications in Session A.
Operational Limits
- Connection-bound: The live query dies with the WebSocket. There is no built-in durability or replay; the client must resubscribe and fetch current state after a reconnect.
- Predicate simplicity: Complex
WHEREclauses, joins, or expensive projections increase server work per write. Push authorization checks, heavy transformations, and joins into regular reads or materialized views. - Permission model: The live query respects the permissions of the authenticated session. If the session lacks
SELECTrights on the table, the live query will return no data and may error depending on the permission mode. - Version sensitivity: Envelope field names, SDK method names, and protocol details change between SurrealDB releases. Pin your client library version and test upgrades. Verify the exact syntax against the documentation for your release line before production use.
Common Mistakes
- Leaking live queries: Forgetting to call
KILLwhen a UI component unmounts or a user logs out leaves the server evaluating the predicate for every write. Always pairLIVE SELECTwith a cleanup step. - Assuming a durable queue: Treating the notification stream as a guaranteed event log leads to data loss during network blips. Design the client to re-sync state after reconnect.
- Over-filtering in the predicate: Putting business logic (for example role and region checks against session variables) inside the live query couples real-time filtering to the write path. Evaluate such filters in the application after receiving a broader notification.
- Ignoring the live-query id: The identifier is returned only once. If you discard it, you cannot stop that specific stream without closing the connection.
Local Verification Steps
Run these steps against a fresh SurrealDB instance to confirm the behavior before integrating it into your stack. You need permission to start a local server and write to its data directory; no production access is involved.
- Start SurrealDB locally, for example
surreal start file://./data, and watch the log output for startup errors. - Open two WebSocket connections (for example
wscat -c ws://localhost:8000/rpc). Authenticate and select the same namespace and database in both, then confirm a normalSELECTworks before testing live behavior. - In Session A, execute
LIVE SELECT * FROM app.user WHERE active = true;and record the returned id. - In Session B, run
CREATE app.user SET name = 'Test', active = true;, thenUPDATEandDELETEthat record. Verify Session A receives three notifications with matching actions. - In Session A, run
KILLwith the saved id. Repeat a write in Session B and confirm no notification arrives. - Close Session A, reopen a new connection, re-authenticate, and run the same
LIVE SELECTagain. Verify a new live-query id is issued and notifications resume.
If any step fails, check the server log for permission errors or syntax issues. This test validates that the live query is session-scoped, terminates cleanly, and requires resubscription after disconnect. Because protocol details vary by release, treat this as a review draft and confirm field names against your installed version.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.