Live Query Promises

Live query promises provide interfaces for subscribing to real-time updates from SurrealDB. There are two variants: ManagedLivePromise for new subscriptions and UnmanagedLivePromise for existing ones.

Returned by: SurrealQueryable.live(), SurrealQueryable.liveOf()

Source: query/live.ts

ManagedLivePromise<T>

A managed live query subscription that the SDK automatically creates and manages.

Returned by: SurrealQueryable.live()

Configuration Methods

.diff()

Configure the subscription to return only patches (diffs) instead of full records on updates.

Method Syntax

livePromise.diff()

Returns: ManagedLivePromise<T> - Chainable promise

Example:

const subscription = await db.live(new Table('users')).diff();

for await (const update of subscription) {
console.log('Diff:', update.diff); // Only changed fields
}

.fields()

Select only specific fields in the live updates.

Method Syntax

livePromise.fields(...fields)

Parameters:

ParameterTypeDescription
fields Field<T>[]Field names to include in updates.

Returns: ManagedLivePromise<T>

Example:

const subscription = await db.live(new Table('users'))
.fields('name', 'email', 'status');

for await (const update of subscription) {
// Only includes specified fields
console.log(update.result); // { name, email, status }
}

.value()

Return only the value of a specific field in updates.

Method Syntax

livePromise.value(field)

Parameters:

ParameterTypeDescription
field Field<T>Field name to extract.

Returns: ManagedLivePromise<T>

Example:

const subscription = await db.live(new Table('users'))
.value('name');

for await (const update of subscription) {
console.log('Name changed:', update.result); // Just the name string
}

.where()

Filter live updates to only receive records matching the condition.

Method Syntax

livePromise.where(condition)

Parameters:

ParameterTypeDescription
condition ExprLikeCondition expression to filter updates (string or Expression object).

Returns: ManagedLivePromise<T>

Example:

const subscription = await db.live(new Table('users'))
.where('age >= 18');

for await (const update of subscription) {
// Only receives updates for users with age >= 18
console.log(update.action, update.result);
}

.fetch()

Fetch related records in live updates.

Method Syntax

livePromise.fetch(...fields)

Parameters:

ParameterTypeDescription
fields Field<T>[]Related fields to fetch.

Returns: ManagedLivePromise<T>

Example:

const subscription = await db.live(new Table('posts'))
.fetch('author', 'comments');

for await (const update of subscription) {
// author and comments are fully populated
console.log('Post by:', update.result.author.name);
}

Live Subscription Methods

Once awaited, a ManagedLivePromise returns a LiveSubscription object:

Iteration

for await (const update of subscription) {
console.log(update.action); // 'CREATE' | 'UPDATE' | 'DELETE'
console.log(update.result); // The record data
}

.kill()

Kill the live query subscription.

await subscription.kill();

UnmanagedLivePromise

An unmanaged subscription to an existing live query by its UUID.

Returned by: SurrealQueryable.liveOf()

Usage

const liveQueryId: Uuid = /* from somewhere */;
const subscription = await db.liveOf(liveQueryId);

for await (const update of subscription) {
console.log(update);
}

Complete Examples

Basic Live Query

import { Surreal, Table } from 'surrealdb';

const db = new Surreal();
await db.connect('ws://localhost:8000');

// Subscribe to all user changes
const subscription = await db.live(new Table('users'));

for await (const update of subscription) {
console.log(`${update.action}:`, update.result);
}

// Clean up when done
await subscription.kill();

Filtered Live Query

// Only receive updates for active users
const subscription = await db.live(new Table('users'))
.where('status = "active"');

for await (const update of subscription) {
if (update.action === 'CREATE') {
console.log('New active user:', update.result);
} else if (update.action === 'UPDATE') {
console.log('Active user updated:', update.result);
} else if (update.action === 'DELETE') {
console.log('Active user deleted:', update.result);
}
}

Live Query with Specific Fields

const subscription = await db.live(new Table('users'))
.fields('name', 'email', 'status')
.where('role = "admin"');

for await (const update of subscription) {
// Only receives name, email, status fields
console.log('Admin update:', update.result);
}

await subscription.kill();

Diff-Based Updates

// Get only changes, not full records
const subscription = await db.live(new Table('users')).diff();

for await (const update of subscription) {
if (update.action === 'UPDATE') {
console.log('Changed fields:', update.diff);
// { email: 'new@example.com' } instead of full record
}
}

Live Query with Relations

const subscription = await db.live(new Table('posts'))
.fetch('author', 'comments')
.where('published = true');

for await (const update of subscription) {
// author is fully populated
console.log('Post by:', update.result.author.name);
console.log('Comments:', update.result.comments.length);
}

Real-time Dashboard

async function monitorUsers(callback: (stats: any) => void) {
const subscription = await db.live(new Table('users'));

const stats = {
created: 0,
updated: 0,
deleted: 0
};

for await (const update of subscription) {
if (update.action === 'CREATE') stats.created++;
else if (update.action === 'UPDATE') stats.updated++;
else if (update.action === 'DELETE') stats.deleted++;

callback(stats);
}
}

monitorUsers((stats) => {
console.log('User stats:', stats);
});

Watch Specific Record

// Subscribe to changes on a specific record
const subscription = await db.live(new RecordId('users', 'john'));

for await (const update of subscription) {
if (update.action === 'UPDATE') {
console.log('John was updated:', update.result);
} else if (update.action === 'DELETE') {
console.log('John was deleted');
break;
}
}

Auto-reconnect Live Query

let subscription: LiveSubscription | null = null;

async function setupLiveQuery() {
subscription = await db.live(new Table('users'))
.where('active = true');

for await (const update of subscription) {
console.log('Update:', update);
}
}

// Handle reconnection
db.subscribe('reconnecting', () => {
console.log('Connection lost, live query will be restored...');
});

db.subscribe('connected', async () => {
console.log('Reconnected, live queries restored automatically');
});

await setupLiveQuery();

Cleanup Pattern

const subscriptions: LiveSubscription[] = [];

// Create multiple subscriptions
subscriptions.push(await db.live(new Table('users')));
subscriptions.push(await db.live(new Table('posts')));
subscriptions.push(await db.live(new Table('comments')));

// Process updates
// ...

// Cleanup all subscriptions
async function cleanup() {
await Promise.all(subscriptions.map(sub => sub.kill()));
console.log('All subscriptions cleaned up');
}

// Call on app shutdown
await cleanup();

Error Handling

try {
const subscription = await db.live(new Table('users'));

try {
for await (const update of subscription) {
await processUpdate(update);
}
} catch (error) {
console.error('Error processing update:', error);
} finally {
await subscription.kill();
}
} catch (error) {
if (error instanceof LiveSubscriptionError) {
console.error('Failed to create subscription:', error);
}
}

Update Message Structure

Each update message conforms to the LiveMessage<T> interface with action, result, and optional diff properties.

Processing Updates

for await (const update of subscription) {
switch (update.action) {
case 'CREATE':
await handleCreate(update.result);
break;
case 'UPDATE':
await handleUpdate(update.result, update.diff);
break;
case 'DELETE':
await handleDelete(update.result);
break;
}
}

See Also