Skip to content

Releases: surrealdb/surrealdb.js

Release v2.0.0-alpha.14

13 Nov 16:19

Choose a tag to compare

Pre-release

Changes since the previous version

  • Added the ability to run the WASM engine in a Web Worker using createWasmWorkerEngines() (#493)
  • Re-introduce support for using BoundQuery .append() as a template literal tag

Full changelog

πŸ“¦ Welcome @surrealdb/wasm and @surrealdb/node!

The existing WebAssembly and Node.js SDK's have been rewritten, updated to support the 2.0 JavaScript SDK, and have been moved into the JavaScript SDK repository.

Going forward, the JS SDK, WASM SDK, and Node.js SDK will be published together, meaning embedded versions of SurrealDB will be kept up-to-date. Both the WASM and Node.js SDK versions will sync their major and minor components with SurrealDB, while the patch is still kept separate. This means a version such as 2.3.5 will use at least SurrealDB 2.3.0.

As an additional bonus, the WASM SDK now also supports running within a Web Worker. This allows you to offload computationally intensive database operations away from the main thread, while keeping your interface responsive.

Wasm

import { Surreal, createRemoteEngines } from "surrealdb";
import { createWasmEngines } from "@surrealdb/wasm";

const db = new Surreal({
    engines: {
        ...createRemoteEngines(),
        ...createWasmEngines(),
        // or for Web Worker based engines
        ...createWasmWorkerEngines()
    },
});

Node.js (+ Bun.js & Deno)

import { Surreal, createRemoteEngines } from "surrealdb";
import { createNodeEngines } from "@surrealdb/node";

const db = new Surreal({
    engines: {
        ...createRemoteEngines(),
        ...createNodeEngines(),
    },
});

βœ‰οΈ Official event listeners

The original SDK allowed for the listening of events through the leaked internal surreal.emitter field. Instead, the updated SDK provides a type-safe surreal.subscribe() function allowing you to listen to events. Invoking .subscribe() now also returns a cleanup function, which unsubscribes the listener when called.

Example

// Subscribe to events
const unsub = surreal.subscribe("connected", () => {
    ...
});

// Unsubscribe
unsub();

🏹 Access internal state

Additional getters have been added to retrieve internal state from the Surreal instance, such as

  • surreal.namespace and surreal.database to obtain the selected NS and DB
  • surreal.params to obtain defined connection params
  • surreal.accesToken and surreal.refreshToken to obtain authentication tokens

Example

await surreal.use({ namespace: surreal.namespace, database: "other-db" });

πŸ”„ Automatic token refreshing

The SDK will now automatically restore or renew authentication when your access token expires or the connection reconnects. When refresh tokens are available, these will be used and exchanged for a fresh token pair, otherwise the SDK falls back to re-using the provided authentication details, or firing an auth event for custom handling. (read more)

In situations where authentication may be provided asynchronously you can now pass a callable function to the authentication property.

Example

const surreal = new Surreal();

await surreal.connect("http://example.com", {
    namespace: "test",
    database: "test",
    renewAccess: true, // default true
    authentication: () => ({
        username: "foo",
        password: "bar",
    })
});

πŸ”‘ Multi-session support

You can now create multiple isolated sessions within a single connection, each with their own namespace, database, variables, and authentication state. The SDK allows you to construct entirely new sessions at any time, or fork an existing session and reuse its state.

Simple example

// Create a new session
const session = await surreal.newSession();

// Use the session
session.signin(...);

// Dispose the session
await session.closeSession();

Forking sessions

const freshSession = await surreal.newSession();

// Clone a session including namespace, database, variables, and auth state
const forkedSession = await freshSession.forkSession();

Await using

await using session = await surreal.newSession();

// JavaScript will automatically close the session at the end of the current scope

πŸ“£ Redesigned live query API

The live query functions provided by the Surreal class have been redesigned to feel more intuitive and natural to use. Additionally, live select queries can now be automatically restarted once the driver reconnects.

The record ID will now also be provided as third argument to your handlers, allowing you to determine the record when listening to patch updates.

Example

// Construct a new live subscription
const live = await surreal.live(new Table("users"));

// Listen to changes
live.subscribe((action, result, record) => {
     ...
});

// Alternatively, iterate messages
for await (const { action, value } of live) {
    ...
}

// Kill the query and stop listening
live.kill();

// Create an unmanaged query from an existing id
const [id] = await surreal.query("LIVE SELECT * FROM users").collect();
const live = await surreal.liveOf(id);

❗ Improved parameter explicitness

Query functions now no longer accept strings as table names. Instead, you must explicitly use the Table class to represent tables. This avoids situations where record ids may be accidentally passed as table names, resulting in confusing results.

Example

// tables.ts
const usersTable = new Table("users");
const productsTable = new Table("products");
...

// main.ts
await surreal.select(usersTable);

πŸ”§ Query builder pattern

In order to provide a more transparent and ergonomic way to configure individual RPC calls, a new builder pattern has been introduced allowing the optional chaining of functions on RPC calls. All existing query functions have received chainable functions to accomplish common tasks such as filtering, limiting, and fetching.

As a side affect, both update and upsert no longer take contents as second argument, instead, you can choose whether you want to .content(), .merge(), .replace(), or .patch() your record(s).

Example

// Select
const record = await db.select(id)
    .fields("age", "firstname", "lastname")
    .fetch("foo");

// Update
await db.update(record).merge({
    hello: "world"
});

πŸ—Ό Query method overhaul

The .query() function has been overhauled to support a wider set of functionality, including the ability to pick response indexes, automatically jsonify results, and stream responses.

Example

// Execute a query with no result
await db.query("UPDATE record SET value = true");

// Execute and collect results
const [user] = await db.query("SELECT * FROM user:foo").collect<[User]>();

// Collect specific results
const [foo, bar] = await db.query("LET $foo = ...; LET $bar = ...; SELECT * FROM $foo; SELECT * FROM $bar")
    .collect<[User, Product]>(2, 3);

// Jsonify responses
const [products] = await db.query("SELECT * FROM product").json().collect<[Product[]]>();

// Stream responses
const stream = surreal.query(`SELECT * FROM foo`).stream();

for await (const frame of stream) {
    if (frame.isValue<Foo>()) {
        //  Process a single value with frame.value typed Foo
    } else if (frame.isDone()) {
        // Handle completion and access stats with frame.stats
    } else if (frame.isError()) {
        // Handle error frame.error
    }
}

Note

SurrealDB currently does not yet support the streaming of individual records, however this API will provide the base for streamed responses in a future update. It is fully backwards compatible with the existing versions of SurrealDB and is now the only way to obtain query stats.

🎨 Expressions API

In order to facilitate working with the .where() function found on multiple query methods, we introduced a new Expressions API to ease the process of composing dynamic expressions. This new API integrates seamlessly with the surql template tag, allowing you to insert param-safe expressions anywhere.

Example

const checkActive = true;

// Query method
await db.select(userTable).where(eq("active", checkActive));

// Custom query
await db.query(surql`SELECT * FROM user WHERE ${eq("active", checkActive)}`);

// Expressions even allow raw insertion
await db.query(surql`SELECT * FROM user ${raw("WHERE active = true")}`);

You can also parse expressions into a string manually using the expr() function

const result: BoundQuery = expr(
    or(
        eq("foo", "bar"),
        false && eq("hello", "world"),
        eq("alpha", "beta"),
        and(
            inside("hello", ["hello"]),
            between("number", 1, 10)
        )
    )
);

πŸ”­ Value encode/decode visitor API

To support advanced use cases and situations where additional processing must be done on SurrealDB value classes, you can now specify a value encode or decode visitor callback in the Surreal constructor. These functions will be invoked for each value received or sent to the engine, and allow you to modify or wrap values before they are collected in responses.

Example

const surreal = new Surreal({
	codecOptions: {
		valueDecodeVisitor(value) {
			if (value instanceof RecordId) {
				return new RecordId("foo", "bar");
			}

			return value;
		},
	},
});

...

const [result] = await surreal.query(`RETURN hello:world`).collect<[RecordId]>();

console.log(result); // foo:bar

πŸ‘€ Diagnostics API

The Diagnostics API allows you to wrap engines and intercept protocol level...

Read more

Releases v2.0.0-alpha.12

03 Nov 16:55

Choose a tag to compare

Pre-release

Changes since the previous version

  • Allow returning null from connect() authentication property
  • Fix authentication never being called more than once

Full changelog

πŸ“¦ Welcome @surrealdb/wasm and @surrealdb/node!

The existing WebAssembly and Node.js SDK's have been rewritten, updated to support the 2.0 JavaScript SDK, and have been moved into the JavaScript SDK repository.

Going forward, the JS SDK, WASM SDK, and Node.js SDK will be published together, meaning embedded versions of SurrealDB will be kept up-to-date. Both the WASM and Node.js SDK versions will sync their major and minor components with SurrealDB, while the patch is still kept separate. This means a version such as 2.3.5 will use at least SurrealDB 2.3.0.

Wasm

import { Surreal, createRemoteEngines } from "surrealdb";
import { createWasmEngines } from "@surrealdb/wasm";

const db = new Surreal({
    engines: {
        ...createRemoteEngines(),
        ...createWasmEngines(),
    },
});

Node.js (+ Bun.js & Deno)

import { Surreal, createRemoteEngines } from "surrealdb";
import { createNodeEngines } from "@surrealdb/node";

const db = new Surreal({
    engines: {
        ...createRemoteEngines(),
        ...createNodeEngines(),
    },
});

βœ‰οΈ Official event listeners

The original SDK allowed for the listening of events through the leaked internal surreal.emitter field. Instead, the updated SDK provides a type-safe surreal.subscribe() function allowing you to listen to events. Invoking .subscribe() now also returns a cleanup function, which unsubscribes the listener when called.

Example

// Subscribe to events
const unsub = surreal.subscribe("connected", () => {
    ...
});

// Unsubscribe
unsub();

🏹 Access internal state

Additional getters have been added to retrieve internal state from the Surreal instance, such as

  • surreal.namespace and surreal.database to obtain the selected NS and DB
  • surreal.params to obtain defined connection params
  • surreal.accesToken and surreal.refreshToken to obtain authentication tokens

Example

await surreal.use({ namespace: surreal.namespace, database: "other-db" });

πŸ”„ Automatic token refreshing

The SDK will now automatically restore or renew authentication when your access token expires or the connection reconnects. When refresh tokens are available, these will be used and exchanged for a fresh token pair, otherwise the SDK falls back to re-using the provided authentication details, or firing an auth event for custom handling. (read more)

In situations where authentication may be provided asynchronously you can now pass a callable function to the authentication property.

Example

const surreal = new Surreal();

await surreal.connect("http://example.com", {
    namespace: "test",
    database: "test",
    renewAccess: true, // default true
    authentication: () => ({
        username: "foo",
        password: "bar",
    })
});

πŸ”‘ Multi-session support

You can now create multiple isolated sessions within a single connection, each with their own namespace, database, variables, and authentication state. The SDK allows you to construct entirely new sessions at any time, or fork an existing session and reuse its state.

Simple example

// Create a new session
const session = await surreal.newSession();

// Use the session
session.signin(...);

// Dispose the session
await session.closeSession();

Forking sessions

const freshSession = await surreal.newSession();

// Clone a session including namespace, database, variables, and auth state
const forkedSession = await freshSession.forkSession();

Await using

await using session = await surreal.newSession();

// JavaScript will automatically close the session at the end of the current scope

πŸ“£ Redesigned live query API

The live query functions provided by the Surreal class have been redesigned to feel more intuitive and natural to use. Additionally, live select queries can now be automatically restarted once the driver reconnects.

The record ID will now also be provided as third argument to your handlers, allowing you to determine the record when listening to patch updates.

Example

// Construct a new live subscription
const live = await surreal.live(new Table("users"));

// Listen to changes
live.subscribe((action, result, record) => {
     ...
});

// Alternatively, iterate messages
for await (const { action, value } of live) {
    ...
}

// Kill the query and stop listening
live.kill();

// Create an unmanaged query from an existing id
const [id] = await surreal.query("LIVE SELECT * FROM users").collect();
const live = await surreal.liveOf(id);

❗ Improved parameter explicitness

Query functions now no longer accept strings as table names. Instead, you must explicitly use the Table class to represent tables. This avoids situations where record ids may be accidentally passed as table names, resulting in confusing results.

Example

// tables.ts
const usersTable = new Table("users");
const productsTable = new Table("products");
...

// main.ts
await surreal.select(usersTable);

πŸ”§ Query builder pattern

In order to provide a more transparent and ergonomic way to configure individual RPC calls, a new builder pattern has been introduced allowing the optional chaining of functions on RPC calls. All existing query functions have received chainable functions to accomplish common tasks such as filtering, limiting, and fetching.

As a side affect, both update and upsert no longer take contents as second argument, instead, you can choose whether you want to .content(), .merge(), .replace(), or .patch() your record(s).

Example

// Select
const record = await db.select(id)
    .fields("age", "firstname", "lastname")
    .fetch("foo");

// Update
await db.update(record).merge({
    hello: "world"
});

πŸ—Ό Query method overhaul

The .query() function has been overhauled to support a wider set of functionality, including the ability to pick response indexes, automatically jsonify results, and stream responses.

Example

// Execute a query with no result
await db.query("UPDATE record SET value = true");

// Execute and collect results
const [user] = await db.query("SELECT * FROM user:foo").collect<[User]>();

// Collect specific results
const [foo, bar] = await db.query("LET $foo = ...; LET $bar = ...; SELECT * FROM $foo; SELECT * FROM $bar")
    .collect<[User, Product]>(2, 3);

// Jsonify responses
const [products] = await db.query("SELECT * FROM product").json().collect<[Product[]]>();

// Stream responses
const stream = surreal.query(`SELECT * FROM foo`).stream();

for await (const frame of stream) {
    if (frame.isValue<Foo>()) {
        //  Process a single value with frame.value typed Foo
    } else if (frame.isDone()) {
        // Handle completion and access stats with frame.stats
    } else if (frame.isError()) {
        // Handle error frame.error
    }
}

Note

SurrealDB currently does not yet support the streaming of individual records, however this API will provide the base for streamed responses in a future update. It is fully backwards compatible with the existing versions of SurrealDB and is now the only way to obtain query stats.

🎨 Expressions API

In order to facilitate working with the .where() function found on multiple query methods, we introduced a new Expressions API to ease the process of composing dynamic expressions. This new API integrates seamlessly with the surql template tag, allowing you to insert param-safe expressions anywhere.

Example

const checkActive = true;

// Query method
await db.select(userTable).where(eq("active", checkActive));

// Custom query
await db.query(surql`SELECT * FROM user WHERE ${eq("active", checkActive)}`);

// Expressions even allow raw insertion
await db.query(surql`SELECT * FROM user ${raw("WHERE active = true")}`);

You can also parse expressions into a string manually using the expr() function

const result: BoundQuery = expr(
    or(
        eq("foo", "bar"),
        false && eq("hello", "world"),
        eq("alpha", "beta"),
        and(
            inside("hello", ["hello"]),
            between("number", 1, 10)
        )
    )
);

πŸ”­ Value encode/decode visitor API

To support advanced use cases and situations where additional processing must be done on SurrealDB value classes, you can now specify a value encode or decode visitor callback in the Surreal constructor. These functions will be invoked for each value received or sent to the engine, and allow you to modify or wrap values before they are collected in responses.

Example

const surreal = new Surreal({
	codecOptions: {
		valueDecodeVisitor(value) {
			if (value instanceof RecordId) {
				return new RecordId("foo", "bar");
			}

			return value;
		},
	},
});

...

const [result] = await surreal.query(`RETURN hello:world`).collect<[RecordId]>();

console.log(result); // foo:bar

πŸ‘€ Diagnostics API

The Diagnostics API allows you to wrap engines and intercept protocol level communication. This is useful for debugging queries, analysing SDK behaviour, measuring event timings, and other advanced use cases.

Since this API is implemented in the form of a wrapper engine, no further overhead is added to the SDK unless used. We do however discourage use of this API in production as it may affect performance negatively and the events are consi...

Read more

Release v2.0.0-alpha.11

03 Nov 11:44

Choose a tag to compare

Pre-release

Changes since the previous version

  • Added multi-session support
    • Each session has an isolated namespace, database, variables, and auth state
    • New sessions can be created with Surreal#newSession()
    • Existing sessions, including the default session, can be forked with SurrealSession#forkSession(), which means the namespace, database, variables, and auth state will be cloned
    • Each session publishes its own using and auth events
    • Sessions can be closed with SurrealSession#closeSession()
    • Added a Surreal#sessions() method to list out all server-recognised sessions on the current connection
  • Added refresh token support
    • Works automatically when refresh tokens are configured in SurrealDB
    • Refresh tokens are used to refresh access tokens automatically
    • Subscribe to the auth event to obtain and store new tokens each time they are refreshed
    • Added the ability to pass existing access + refresh tokens to SurrealSession#authenticate() in order to refresh them
  • Rework authentication logic
    • Calling any of the auth methods (signin, signup, or authenticate) now prevents the connect() authentication property from being used to restore authentication
    • The SDK will now attempt to re-use access tokens which are still valid
    • Updated SurrealSession#authenticate() to support refresh tokens
    • Replaced the invalidated and authenticated events with a unified auth event which is fired when a session is authenticated, an authenticated session is refreshed, or an authenticated session is invalidated.
    • Made the connect() authentication property only accept system user and token details
  • Added a feature system to properly track engine and version support
    • Exposed in the public api as Surreal#isFeatureSupported()
  • Improved and refactored errors with consistent names and messages
    • This means all errors are now suffixed with Error
  • Added support for Set values
  • Added support for defining a custom fetch() implementation
  • Added the ability to use interface types in query generics
  • Fixed Surreal#select(RecordId) missing undefined as return type
  • Fixed invalid dates not being handled correctly
  • Fixed Symbol.toStringTag to use a getter
  • Fixed namespace/database tracking getting out of sync

Full changelog

πŸ“¦ Welcome @surrealdb/wasm and @surrealdb/node!

The existing WebAssembly and Node.js SDK's have been rewritten, updated to support the 2.0 JavaScript SDK, and have been moved into the JavaScript SDK repository.

Going forward, the JS SDK, WASM SDK, and Node.js SDK will be published together, meaning embedded versions of SurrealDB will be kept up-to-date. Both the WASM and Node.js SDK versions will sync their major and minor components with SurrealDB, while the patch is still kept separate. This means a version such as 2.3.5 will use at least SurrealDB 2.3.0.

Wasm

import { Surreal, createRemoteEngines } from "surrealdb";
import { createWasmEngines } from "@surrealdb/wasm";

const db = new Surreal({
    engines: {
        ...createRemoteEngines(),
        ...createWasmEngines(),
    },
});

Node.js (+ Bun.js & Deno)

import { Surreal, createRemoteEngines } from "surrealdb";
import { createNodeEngines } from "@surrealdb/node";

const db = new Surreal({
    engines: {
        ...createRemoteEngines(),
        ...createNodeEngines(),
    },
});

βœ‰οΈ Official event listeners

The original SDK allowed for the listening of events through the leaked internal surreal.emitter field. Instead, the updated SDK provides a type-safe surreal.subscribe() function allowing you to listen to events. Invoking .subscribe() now also returns a cleanup function, which unsubscribes the listener when called.

Example

// Subscribe to events
const unsub = surreal.subscribe("connected", () => {
    ...
});

// Unsubscribe
unsub();

🏹 Access internal state

Additional getters have been added to retrieve internal state from the Surreal instance, such as

  • surreal.namespace and surreal.database to obtain the selected NS and DB
  • surreal.params to obtain defined connection params
  • surreal.accesToken and surreal.refreshToken to obtain authentication tokens

Example

await surreal.use({ namespace: surreal.namespace, database: "other-db" });

πŸ”„ Automatic token refreshing

The SDK will now automatically restore or renew authentication when your access token expires or the connection reconnects. When refresh tokens are available, these will be used and exchanged for a fresh token pair, otherwise the SDK falls back to re-using the provided authentication details, or firing an auth event for custom handling. (read more)

In situations where authentication may be provided asynchronously you can now pass a callable function to the authentication property.

Example

const surreal = new Surreal();

await surreal.connect("http://example.com", {
    namespace: "test",
    database: "test",
    renewAccess: true, // default true
    authentication: () => ({
        username: "foo",
        password: "bar",
    })
});

πŸ”‘ Multi-session support

You can now create multiple isolated sessions within a single connection, each with their own namespace, database, variables, and authentication state. The SDK allows you to construct entirely new sessions at any time, or fork an existing session and reuse its state.

Simple example

// Create a new session
const session = await surreal.newSession();

// Use the session
session.signin(...);

// Dispose the session
await session.closeSession();

Forking sessions

const freshSession = await surreal.newSession();

// Clone a session including namespace, database, variables, and auth state
const forkedSession = await freshSession.forkSession();

Await using

await using session = await surreal.newSession();

// JavaScript will automatically close the session at the end of the current scope

πŸ“£ Redesigned live query API

The live query functions provided by the Surreal class have been redesigned to feel more intuitive and natural to use. Additionally, live select queries can now be automatically restarted once the driver reconnects.

The record ID will now also be provided as third argument to your handlers, allowing you to determine the record when listening to patch updates.

Example

// Construct a new live subscription
const live = await surreal.live(new Table("users"));

// Listen to changes
live.subscribe((action, result, record) => {
     ...
});

// Alternatively, iterate messages
for await (const { action, value } of live) {
    ...
}

// Kill the query and stop listening
live.kill();

// Create an unmanaged query from an existing id
const [id] = await surreal.query("LIVE SELECT * FROM users").collect();
const live = await surreal.liveOf(id);

❗ Improved parameter explicitness

Query functions now no longer accept strings as table names. Instead, you must explicitly use the Table class to represent tables. This avoids situations where record ids may be accidentally passed as table names, resulting in confusing results.

Example

// tables.ts
const usersTable = new Table("users");
const productsTable = new Table("products");
...

// main.ts
await surreal.select(usersTable);

πŸ”§ Query builder pattern

In order to provide a more transparent and ergonomic way to configure individual RPC calls, a new builder pattern has been introduced allowing the optional chaining of functions on RPC calls. All existing query functions have received chainable functions to accomplish common tasks such as filtering, limiting, and fetching.

As a side affect, both update and upsert no longer take contents as second argument, instead, you can choose whether you want to .content(), .merge(), .replace(), or .patch() your record(s).

Example

// Select
const record = await db.select(id)
    .fields("age", "firstname", "lastname")
    .fetch("foo");

// Update
await db.update(record).merge({
    hello: "world"
});

πŸ—Ό Query method overhaul

The .query() function has been overhauled to support a wider set of functionality, including the ability to pick response indexes, automatically jsonify results, and stream responses.

Example

// Execute a query with no result
await db.query("UPDATE record SET value = true");

// Execute and collect results
const [user] = await db.query("SELECT * FROM user:foo").collect<[User]>();

// Collect specific results
const [foo, bar] = await db.query("LET $foo = ...; LET $bar = ...; SELECT * FROM $foo; SELECT * FROM $bar")
    .collect<[User, Product]>(2, 3);

// Jsonify responses
const [products] = await db.query("SELECT * FROM product").json().collect<[Product[]]>();

// Stream responses
const stream = surreal.query(`SELECT * FROM foo`).stream();

for await (const frame of stream) {
    if (frame.isValue<Foo>()) {
        //  Process a single value with frame.value typed Foo
    } else if (frame.isDone()) {
        // Handle completion and access stats with frame.stats
    } else if (frame.isError()) {
        // Handle error frame.error
    }
}

Note

SurrealDB currently does not yet support the streaming of individual records, however this API will provide the base for streamed responses in a future update. It is fully backwards compatible with the existing versions of SurrealDB and is now the only way to obtain query stats.

🎨 Expressions API

In order to facilitate working with the .where() function found on multipl...

Read more

Release v2.0.0-alpha.10

24 Oct 14:43

Choose a tag to compare

Pre-release

Changes since the previous version

  • Fixed an issue where zero-DateTimes encode to an invalid format
  • Fixed the toSurqlString() formatting of FileRef
  • Added support for obtaining query type from DoneFrame
  • Added a Value encode/decode visitor API
    • This allows you to process incoming and outgoing value instances during the encode or decode process
  • Added a Diagnostics API
    • This allows you to tap into the internal communication stream in order to debug queries and watch RPC calls

Full changelog

πŸ“¦ Welcome @surrealdb/wasm and @surrealdb/node!

The existing WebAssembly and Node.js SDK's have been rewritten, updated to support the 2.0 JavaScript SDK, and have been moved into the JavaScript SDK repository.

Going forward, the JS SDK, WASM SDK, and Node.js SDK will be published together, meaning embedded versions of SurrealDB will be kept up-to-date. Both the WASM and Node.js SDK versions will sync their major and minor components with SurrealDB, while the patch is still kept separate. This means a version such as 2.3.5 will use at least SurrealDB 2.3.0.

Wasm

import { Surreal, createRemoteEngines } from "surrealdb";
import { createWasmEngines } from "@surrealdb/wasm";

const db = new Surreal({
    engines: {
        ...createRemoteEngines(),
        ...createWasmEngines(),
    },
});

Node.js (+ Bun.js & Deno)

import { Surreal, createRemoteEngines } from "surrealdb";
import { createNodeEngines } from "@surrealdb/node";

const db = new Surreal({
    engines: {
        ...createRemoteEngines(),
        ...createNodeEngines(),
    },
});

βœ‰οΈ Official event listeners

The original SDK allowed for the listening of events through the leaked internal surreal.emitter field. Instead, the updated SDK provides a type-safe surreal.subscribe() function allowing you to listen to events. Invoking .subscribe() now also returns a cleanup function, which unsubscribes the listener when called.

Example

// Subscribe to events
const unsub = surreal.subscribe("connected", () => {
    ...
});

// Unsubscribe
unsub();

🏹 Access internal state

Additional getters have been added to retrieve internal state from the Surreal instance, such as

  • surreal.namespace and surreal.database to obtain the selected NS and DB
  • surreal.params to obtain defined connection params
  • surreal.accesToken and surreal.refreshToken to obtain authentication tokens

Example

await surreal.use({ namespace: surreal.namespace, database: "other-db" });

πŸ”„ Automatic token refreshing

SurrealDB will now automatically use the provided authentication values when reconnecting and renewing access tokens. In addition, you can now also provide a callable function to resolve your authentication details on the fly, such as when loading tokens from browser storage.

Access token renewal is now also managed by default, meaning the SDK will automatically request a fresh access token using the configured authentication details once the previous token is set to expire. If necessary, you can even pass a custom renewal hook in order to customize this process.

Example

const surreal = new Surreal();

await surreal.connect("http://example.com", {
    namespace: "test",
    database: "test",
    renewAccess: true, // default true
    authentication: () => ({
        username: "foo",
        password: "bar",
    })
});

πŸ“£ Redesigned live query API

The live query functions provided by the Surreal class have been redesigned to feel more intuitive and natural to use. Additionally, live select queries can now be automatically restarted once the driver reconnects.

The record ID will now also be provided as third argument to your handlers, allowing you to determine the record when listening to patch updates.

Example

// Construct a new live subscription
const live = await surreal.live(new Table("users"));

// Listen to changes
live.subscribe((action, result, record) => {
     ...
});

// Alternatively, iterate messages
for await (const { action, value } of live) {
    ...
}

// Kill the query and stop listening
live.kill();

// Create an unmanaged query from an existing id
const [id] = await surreal.query("LIVE SELECT * FROM users").collect();
const live = await surreal.liveOf(id);

❗ Improved parameter explicitness

Query functions now no longer accept strings as table names. Instead, you must explicitly use the Table class to represent tables. This avoids situations where record ids may be accidentally passed as table names, resulting in confusing results.

Example

// tables.ts
const usersTable = new Table("users");
const productsTable = new Table("products");
...

// main.ts
await surreal.select(usersTable);

πŸ”§ Query builder pattern

In order to provide a more transparent and ergonomic way to configure individual RPC calls, a new builder pattern has been introduced allowing the optional chaining of functions on RPC calls. All existing query functions have received chainable functions to accomplish common tasks such as filtering, limiting, and fetching.

As a side affect, both update and upsert no longer take contents as second argument, instead, you can choose whether you want to .content(), .merge(), .replace(), or .patch() your record(s).

Example

// Select
const record = await db.select(id)
    .fields("age", "firstname", "lastname")
    .fetch("foo");

// Update
await db.update(record).merge({
    hello: "world"
});

πŸ—Ό Query method overhaul

The .query() function has been overhauled to support a wider set of functionality, including the ability to pick response indexes, automatically jsonify results, and stream responses.

Example

// Execute a query with no result
await db.query("UPDATE record SET value = true");

// Execute and collect results
const [user] = await db.query("SELECT * FROM user:foo").collect<[User]>();

// Collect specific results
const [foo, bar] = await db.query("LET $foo = ...; LET $bar = ...; SELECT * FROM $foo; SELECT * FROM $bar")
    .collect<[User, Product]>(2, 3);

// Jsonify responses
const [products] = await db.query("SELECT * FROM product").json().collect<[Product[]]>();

// Stream responses
const stream = surreal.query(`SELECT * FROM foo`).stream();

for await (const frame of stream) {
    if (frame.isValue<Foo>()) {
        //  Process a single value with frame.value typed Foo
    } else if (frame.isDone()) {
        // Handle completion and access stats with frame.stats
    } else if (frame.isError()) {
        // Handle error frame.error
    }
}

Note

SurrealDB currently does not yet support the streaming of individual records, however this API will provide the base for streamed responses in a future update. It is fully backwards compatible with the existing versions of SurrealDB and is now the only way to obtain query stats.

🎨 Expressions API

In order to facilitate working with the .where() function found on multiple query methods, we introduced a new Expressions API to ease the process of composing dynamic expressions. This new API integrates seamlessly with the surql template tag, allowing you to insert param-safe expressions anywhere.

Example

const checkActive = true;

// Query method
await db.select(userTable).where(eq("active", checkActive));

// Custom query
await db.query(surql`SELECT * FROM user WHERE ${eq("active", checkActive)}`);

// Expressions even allow raw insertion
await db.query(surql`SELECT * FROM user ${raw("WHERE active = true")}`);

You can also parse expressions into a string manually using the expr() function

const result: BoundQuery = expr(
    or(
        eq("foo", "bar"),
        false && eq("hello", "world"),
        eq("alpha", "beta"),
        and(
            inside("hello", ["hello"]),
            between("number", 1, 10)
        )
    )
);

πŸ”­ Value encode/decode visitor API

To support advanced use cases and situations where additional processing must be done on SurrealDB value classes, you can now specify a value encode or decode visitor callback in the Surreal constructor. These functions will be invoked for each value received or sent to the engine, and allow you to modify or wrap values before they are collected in responses.

Example

const surreal = new Surreal({
	codecOptions: {
		valueDecodeVisitor(value) {
			if (value instanceof RecordId) {
				return new RecordId("foo", "bar");
			}

			return value;
		},
	},
});

...

const [result] = await surreal.query(`RETURN hello:world`).collect<[RecordId]>();

console.log(result); // foo:bar

πŸ‘€ Diagnostics API

The Diagnostics API allows you to wrap engines and intercept protocol level communication. This is useful for debugging queries, analysing SDK behaviour, measuring event timings, and other advanced use cases.

Since this API is implemented in the form of a wrapper engine, no further overhead is added to the SDK unless used. We do however discourage use of this API in production as it may affect performance negatively and the events are considered unstable, meaning they might change between versions.

Example

new Surreal({
	driverOptions: {
		engines: applyDiagnostics(createRemoteEngines(), (event) => {
			console.log(event);
		}),
	},
});

Events contain various bits of information describing the start or completion of operations

  • Each event consists of at least a type, key, and phase property
  • The type property...
Read more

Release v2.0.0-alpha.9

13 Oct 16:36

Choose a tag to compare

Pre-release

Changes since the previous version

  • Added an .isSingle property to ValueFrame to check whether a value is the sole result of a query
  • Fixed single frames not being followed up by a DoneFrame
  • Export the abstract Value class
  • Assure .connect() always changes the current state to connecting
  • Gracefully handle .connect() being called sequentially without awaiting
  • Changed the engines constructor option to no longer forcefully configure remote engines
    • This means when configuring additional engines, such as the Wasm or Node engines, you will also need to manually include createRemoteEngines()

Full changelog

πŸ“¦ Welcome @surrealdb/wasm and @surrealdb/node!

The existing WebAssembly and Node.js SDK's have been rewritten, updated to support the 2.0 JavaScript SDK, and have been moved into the JavaScript SDK repository.

Going forward, the JS SDK, WASM SDK, and Node.js SDK will be published together, meaning embedded versions of SurrealDB will be kept up-to-date. Both the WASM and Node.js SDK versions will sync their major and minor components with SurrealDB, while the patch is still kept separate. This means a version such as 2.3.5 will use at least SurrealDB 2.3.0.

Wasm

import { Surreal, createRemoteEngines } from "surrealdb";
import { createWasmEngines } from "@surrealdb/wasm";

const db = new Surreal({
    engines: {
        ...createRemoteEngines(),
        ...createWasmEngines(),
    },
});

Node.js (+ Bun.js & Deno)

import { Surreal, createRemoteEngines } from "surrealdb";
import { createNodeEngines } from "@surrealdb/node";

const db = new Surreal({
    engines: {
        ...createRemoteEngines(),
        ...createNodeEngines(),
    },
});

βœ‰οΈ Official event listeners

The original SDK allowed for the listening of events through the leaked internal surreal.emitter field. Instead, the updated SDK provides a type-safe surreal.subscribe() function allowing you to listen to events. Invoking .subscribe() now also returns a cleanup function, which unsubscribes the listener when called.

Example

// Subscribe to events
const unsub = surreal.subscribe("connected", () => {
    ...
});

// Unsubscribe
unsub();

🏹 Access internal state

Additional getters have been added to retrieve internal state from the Surreal instance, such as

  • surreal.namespace and surreal.database to obtain the selected NS and DB
  • surreal.params to obtain defined connection params
  • surreal.accesToken and surreal.refreshToken to obtain authentication tokens

Example

await surreal.use({ namespace: surreal.namespace, database: "other-db" });

πŸ”„ Automatic token refreshing

SurrealDB will now automatically use the provided authentication values when reconnecting and renewing access tokens. In addition, you can now also provide a callable function to resolve your authentication details on the fly, such as when loading tokens from browser storage.

Access token renewal is now also managed by default, meaning the SDK will automatically request a fresh access token using the configured authentication details once the previous token is set to expire. If necessary, you can even pass a custom renewal hook in order to customize this process.

Example

const surreal = new Surreal();

await surreal.connect("http://example.com", {
    namespace: "test",
    database: "test",
    renewAccess: true, // default true
    authentication: () => ({
        username: "foo",
        password: "bar",
    })
});

πŸ“£ Redesigned live query API

The live query functions provided by the Surreal class have been redesigned to feel more intuitive and natural to use. Additionally, live select queries can now be automatically restarted once the driver reconnects.

The record ID will now also be provided as third argument to your handlers, allowing you to determine the record when listening to patch updates.

Example

// Construct a new live subscription
const live = await surreal.live(new Table("users"));

// Listen to changes
live.subscribe((action, result, record) => {
     ...
});

// Alternatively, iterate messages
for await (const { action, value } of live) {
    ...
}

// Kill the query and stop listening
live.kill();

// Create an unmanaged query from an existing id
const [id] = await surreal.query("LIVE SELECT * FROM users").collect();
const live = await surreal.liveOf(id);

❗ Improved parameter explicitness

Query functions now no longer accept strings as table names. Instead, you must explicitly use the Table class to represent tables. This avoids situations where record ids may be accidentally passed as table names, resulting in confusing results.

Example

// tables.ts
const usersTable = new Table("users");
const productsTable = new Table("products");
...

// main.ts
await surreal.select(usersTable);

πŸ”§ Query builder pattern

In order to provide a more transparent and ergonomic way to configure individual RPC calls, a new builder pattern has been introduced allowing the optional chaining of functions on RPC calls. All existing query functions have received chainable functions to accomplish common tasks such as filtering, limiting, and fetching.

As a side affect, both update and upsert no longer take contents as second argument, instead, you can choose whether you want to .content(), .merge(), .replace(), or .patch() your record(s).

Example

// Select
const record = await db.select(id)
    .fields("age", "firstname", "lastname")
    .fetch("foo");

// Update
await db.update(record).merge({
    hello: "world"
});

πŸ—Ό Query method overhaul

The .query() function has been overhauled to support a wider set of functionality, including the ability to pick response indexes, automatically jsonify results, and stream responses.

// Execute a query with no result
await db.query("UPDATE record SET value = true");

// Execute and collect results
const [user] = await db.query("SELECT * FROM user:foo").collect<[User]>();

// Collect specific results
const [foo, bar] = await db.query("LET $foo = ...; LET $bar = ...; SELECT * FROM $foo; SELECT * FROM $bar")
    .collect<[User, Product]>(2, 3);

// Jsonify responses
const [products] = await db.query("SELECT * FROM product").json().collect<[Product[]]>();

// Stream responses
const stream = surreal.query(`SELECT * FROM foo`).stream();

for await (const frame of stream) {
    if (frame.isValue<Foo>()) {
        //  Process a single value with frame.value typed Foo
    } else if (frame.isDone()) {
        // Handle completion and access stats with frame.stats
    } else if (frame.isError()) {
        // Handle error frame.error
    }
}

Note

SurrealDB currently does not yet support the streaming of individual records, however this API will provide the base for streamed responses in a future update. It is fully backwards compatible with the existing versions of SurrealDB and is now the only way to obtain query stats.

🎨 Expressions API

In order to facilitate working with the .where() function found on multiple query methods, we introduced a new Expressions API to ease the process of composing dynamic expressions. This new API integrates seamlessly with the surql template tag, allowing you to insert param-safe expressions anywhere.

const checkActive = true;

// Query method
await db.select(userTable).where(eq("active", checkActive));

// Custom query
await db.query(surql`SELECT * FROM user WHERE ${eq("active", checkActive)}`);

// Expressions even allow raw insertion
await db.query(surql`SELECT * FROM user ${raw("WHERE active = true")}`);

You can also parse expressions into a string manually using the expr() function

const result: BoundQuery = expr(
    or(
        eq("foo", "bar"),
        false && eq("hello", "world"),
        eq("alpha", "beta"),
        and(
            inside("hello", ["hello"]),
            between("number", 1, 10)
        )
    )
);

βš™οΈ Separation of concerns

The SDK has been rebuilt in a way which allows for optimal code-reuse while still allowing flexibility for future RPC protocol iterations. This is done by dividing the internal SDK logic into three distinct layers.

Engines

Much like in in the original SDK, engines allow you to connect to a specific datastore, whether it be over HTTP or WS, or embedded through @surrealdb/wasm. However, this has now been streamlined further so that engines are only exclusively responsible for communicating and delivering RPC messages to a specific datastore, while implementing the new SurrealDB Protocol pattern.

Controller

The connection controller is responsible for tracking the local connection state and synchronising it with the SurrealDB instance through the instantiated engine implementation. This includes tracking the selected namespace and database, handling authentication, and performing version checks.

Surreal

Much like in the original SDK the Surreal class represents the public API and exposes all necessary public functionality, while wrapping the underlying connection controller.

πŸ“š Updated documentation

TypeScript signatures and documentations have been fixed and updated to correctly describe different arguments and overloads.

Release v2.0.0-alpha.8

26 Sep 13:35

Choose a tag to compare

Pre-release

Changes since the previous version

  • Fixed Wasm and Node engines throwing when closed during connecting state
  • Added support for live select in the Node engine
  • Added a Surreal option to use native date objects
  • Added the ability to customise internal codec registries
  • Updated SurrealDB

Full changelog

πŸ“¦ Welcome @surrealdb/wasm and @surrealdb/node!

The existing WebAssembly and Node.js SDK's have been rewritten, updated to support the 2.0 JavaScript SDK, and have been moved into the JavaScript SDK repository.

Going forward, the JS SDK, WASM SDK, and Node.js SDK will be versioned and published together, meaning compatibility will be guaranteed between SDKs sharing the same version. In addition, both the WASM and Node.js SDKs will now contain their respective SurrealDB version as semver metadata, allowing you to easily identify the embedded version of SurrealDB.

βœ‰οΈ Official event listeners

The original SDK allowed for the listening of events through the leaked internal surreal.emitter field. Instead, the updated SDK provides a type-safe surreal.subscribe() function allowing you to listen to events. Invoking .subscribe() now also returns a cleanup function, which unsubscribes the listener when called.

Example

// Subscribe to events
const unsub = surreal.subscribe("connected", () => {
    ...
});

// Unsubscribe
unsub();

🏹 Access internal state

Additional getters have been added to retrieve internal state from the Surreal instance, such as

  • surreal.namespace and surreal.database to obtain the selected NS and DB
  • surreal.params to obtain defined connection params
  • surreal.accesToken and surreal.refreshToken to obtain authentication tokens

Example

await surreal.use({ namespace: surreal.namespace, database: "other-db" });

πŸ”„ Automatic token refreshing

SurrealDB will now automatically use the provided authentication values when reconnecting and renewing access tokens. In addition, you can now also provide a callable function to resolve your authentication details on the fly, such as when loading tokens from browser storage.

Access token renewal is now also managed by default, meaning the SDK will automatically request a fresh access token using the configured authentication details once the previous token is set to expire. If necessary, you can even pass a custom renewal hook in order to customize this process.

Example

const surreal = new Surreal();

await surreal.connect("http://example.com", {
    namespace: "test",
    database: "test",
    renewAccess: true, // default true
    authentication: () => ({
        username: "foo",
        password: "bar",
    })
});

πŸ“£ Redesigned live query API

The live query functions provided by the Surreal class have been redesigned to feel more intuitive and natural to use. Additionally, live select queries can now be automatically restarted once the driver reconnects.

The record ID will now also be provided as third argument to your handlers, allowing you to determine the record when listening to patch updates.

Example

// Construct a new live subscription
const live = await surreal.live(new Table("users"));

// Listen to changes
live.subscribe((action, result, record) => {
     ...
});

// Alternatively, iterate messages
for await (const { action, value } of live) {
    ...
}

// Kill the query and stop listening
live.kill();

// Create an unmanaged query from an existing id
const [id] = await surreal.query("LIVE SELECT * FROM users").collect();
const live = await surreal.liveOf(id);

❗ Improved parameter explicitness

Query functions now no longer accept strings as table names. Instead, you must explicitly use the Table class to represent tables. This avoids situations where record ids may be accidentally passed as table names, resulting in confusing results.

Example

// tables.ts
const usersTable = new Table("users");
const productsTable = new Table("products");
...

// main.ts
await surreal.select(usersTable);

πŸ”§ Query builder pattern

In order to provide a more transparent and ergonomic way to configure individual RPC calls, a new builder pattern has been introduced allowing the optional chaining of functions on RPC calls. All existing query functions have received chainable functions to accomplish common tasks such as filtering, limiting, and fetching.

As a side affect, both update and upsert no longer take contents as second argument, instead, you can choose whether you want to .content(), .merge(), .replace(), or .patch() your record(s).

Example

// Select
const record = await db.select(id)
    .fields("age", "firstname", "lastname")
    .fetch("foo");

// Update
await db.update(record).merge({
    hello: "world"
});

πŸ—Ό Query method overhaul

The .query() function has been overhauled to support a wider set of functionality, including the ability to pick response indexes, automatically jsonify results, and stream responses.

// Execute a query with no result
await db.query("UPDATE record SET value = true");

// Execute and collect results
const [user] = await db.query("SELECT * FROM user:foo").collect<[User]>();

// Collect specific results
const [foo, bar] = await db.query("LET $foo = ...; LET $bar = ...; SELECT * FROM $foo; SELECT * FROM $bar")
    .collect<[User, Product]>(2, 3);

// Jsonify responses
const [products] = await db.query("SELECT * FROM product").json().collect<[Product[]]>();

// Stream responses
const stream = surreal.query(`SELECT * FROM foo`).stream();

for await (const frame of stream) {
    if (frame.isValue<Foo>()) {
        //  Process a single value with frame.value typed Foo
    } else if (frame.isDone()) {
        // Handle completion and access stats with frame.stats
    } else if (frame.isError()) {
        // Handle error frame.error
    }
}

Note

SurrealDB currently does not yet support the streaming of individual records, however this API will provide the base for streamed responses in a future update. It is fully backwards compatible with the existing versions of SurrealDB and is now the only way to obtain query stats.

🎨 Expressions API

In order to facilitate working with the .where() function found on multiple query methods, we introduced a new Expressions API to ease the process of composing dynamic expressions. This new API integrates seamlessly with the surql template tag, allowing you to insert param-safe expressions anywhere.

const checkActive = true;

// Query method
await db.select(userTable).where(eq("active", checkActive));

// Custom query
await db.query(surql`SELECT * FROM user WHERE ${eq("active", checkActive)}`);

// Expressions even allow raw insertion
await db.query(surql`SELECT * FROM user ${raw("WHERE active = true")}`);

You can also parse expressions into a string manually using the expr() function

const result: BoundQuery = expr(
    or(
        eq("foo", "bar"),
        false && eq("hello", "world"),
        eq("alpha", "beta"),
        and(
            inside("hello", ["hello"]),
            between("number", 1, 10)
        )
    )
);

βš™οΈ Separation of concerns

The SDK has been rebuilt in a way which allows for optimal code-reuse while still allowing flexibility for future RPC protocol iterations. This is done by dividing the internal SDK logic into three distinct layers.

Engines

Much like in in the original SDK, engines allow you to connect to a specific datastore, whether it be over HTTP or WS, or embedded through @surrealdb/wasm. However, this has now been streamlined further so that engines are only exclusively responsible for communicating and delivering RPC messages to a specific datastore, while implementing the new SurrealDB Protocol pattern.

Controller

The connection controller is responsible for tracking the local connection state and synchronising it with the SurrealDB instance through the instantiated engine implementation. This includes tracking the selected namespace and database, handling authentication, and performing version checks.

Surreal

Much like in the original SDK the Surreal class represents the public API and exposes all necessary public functionality, while wrapping the underlying connection controller.

πŸ“š Updated documentation

TypeScript signatures and documentations have been fixed and updated to correctly describe different arguments and overloads.

Release v2.0.0-alpha.7

09 Sep 14:29
06d202a

Choose a tag to compare

Pre-release

Changes since the previous version

  • Moved the WASM and Node SDKs into the JavaScript SDK monorepo
  • Updated @surrealdb/wasm and @surrealdb/node with support for v2.0.0-alpha.5
  • The SurrealV1 and SurrealV2 classes have been condensed back into a single Surreal class
  • The engine API has been overhauled to conform to the SurrealDB Protocol
  • Query functions have been updated to support immediate collection, response picking, and response streaming
  • Added chainable functions to specify many additional query clauses
  • Live queries now implement the AsyncIterable spec, while continuing to offer a .subscribe() helper function
  • Prepared Queries have been replaced with the concept of Bound Queries
  • All queries now support automatic JSON result serialisation with .json()
  • The .info() method has been renamed to .auth() in order to clarify its purpose
  • Replaced the representations of datetimes with a high-precision custom DateTime class

Full changelog

πŸ“¦ Welcome @surrealdb/wasm and @surrealdb/node!

The existing WebAssembly and Node.js SDK's have been rewritten, updated to support the 2.0 JavaScript SDK, and have been moved into the JavaScript SDK repository.

Going forward, the JS SDK, WASM SDK, and Node.js SDK will be versioned and published together, meaning compatibility will be guaranteed between SDKs sharing the same version. In addition, both the WASM and Node.js SDKs will now contain their respective SurrealDB version as semver metadata, allowing you to easily identify the embedded version of SurrealDB.

βœ‰οΈ Official event listeners

The original SDK allowed for the listening of events through the leaked internal surreal.emitter field. Instead, the updated SDK provides a type-safe surreal.subscribe() function allowing you to listen to events. Invoking .subscribe() now also returns a cleanup function, which unsubscribes the listener when called.

Example

// Subscribe to events
const unsub = surreal.subscribe("connected", () => {
    ...
});

// Unsubscribe
unsub();

🏹 Access internal state

Additional getters have been added to retrieve internal state from the Surreal instance, such as

  • surreal.namespace and surreal.database to obtain the selected NS and DB
  • surreal.params to obtain defined connection params
  • surreal.accesToken and surreal.refreshToken to obtain authentication tokens

Example

await surreal.use({ namespace: surreal.namespace, database: "other-db" });

πŸ”„ Automatic token refreshing

SurrealDB will now automatically use the provided authentication values when reconnecting and renewing access tokens. In addition, you can now also provide a callable function to resolve your authentication details on the fly, such as when loading tokens from browser storage.

Access token renewal is now also managed by default, meaning the SDK will automatically request a fresh access token using the configured authentication details once the previous token is set to expire. If necessary, you can even pass a custom renewal hook in order to customize this process.

Example

const surreal = new Surreal();

await surreal.connect("http://example.com", {
    namespace: "test",
    database: "test",
    renewAccess: true, // default true
    authentication: () => ({
        username: "foo",
        password: "bar",
    })
});

πŸ“£ Redesigned live query API

The live query functions provided by the Surreal class have been redesigned to feel more intuitive and natural to use. Additionally, live select queries can now be automatically restarted once the driver reconnects.

The record ID will now also be provided as third argument to your handlers, allowing you to determine the record when listening to patch updates.

Example

// Construct a new live subscription
const live = await surreal.live(new Table("users"));

// Listen to changes
live.subscribe((action, result, record) => {
     ...
});

// Alternatively, iterate messages
for await (const { action, value } of live) {
    ...
}

// Kill the query and stop listening
live.kill();

// Create an unmanaged query from an existing id
const [id] = await surreal.query("LIVE SELECT * FROM users").collect();
const live = await surreal.liveOf(id);

❗ Improved parameter explicitness

Query functions now no longer accept strings as table names. Instead, you must explicitly use the Table class to represent tables. This avoids situations where record ids may be accidentally passed as table names, resulting in confusing results.

Example

// tables.ts
const usersTable = new Table("users");
const productsTable = new Table("products");
...

// main.ts
await surreal.select(usersTable);

πŸ”§ Query builder pattern

In order to provide a more transparent and ergonomic way to configure individual RPC calls, a new builder pattern has been introduced allowing the optional chaining of functions on RPC calls. All existing query functions have received chainable functions to accomplish common tasks such as filtering, limiting, and fetching.

As a side affect, both update and upsert no longer take contents as second argument, instead, you can choose whether you want to .content(), .merge(), .replace(), or .patch() your record(s).

Example

// Select
const record = await db.select(id)
    .fields("age", "firstname", "lastname")
    .fetch("foo");

// Update
await db.update(record).merge({
    hello: "world"
});

πŸ—Ό Query method overhaul

The .query() function has been overhauled to support a wider set of functionality, including the ability to pick response indexes, automatically jsonify results, and stream responses.

// Execute a query with no result
await db.query("UPDATE record SET value = true");

// Execute and collect results
const [user] = await db.query("SELECT * FROM user:foo").collect<[User]>();

// Collect specific results
const [foo, bar] = await db.query("LET $foo = ...; LET $bar = ...; SELECT * FROM $foo; SELECT * FROM $bar")
    .collect<[User, Product]>(2, 3);

// Jsonify responses
const [products] = await db.query("SELECT * FROM product").json().collect<[Product[]]>();

// Stream responses
const stream = surreal.query(`SELECT * FROM foo`).stream();

for await (const frame of stream) {
    if (frame.isValue<Foo>()) {
        //  Process a single value with frame.value typed Foo
    } else if (frame.isDone()) {
        // Handle completion and access stats with frame.stats
    } else if (frame.isError()) {
        // Handle error frame.error
    }
}

Note

SurrealDB currently does not yet support the streaming of individual records, however this API will provide the base for streamed responses in a future update. It is fully backwards compatible with the existing versions of SurrealDB and is now the only way to obtain query stats.

🎨 Expressions API

In order to facilitate working with the .where() function found on multiple query methods, we introduced a new Expressions API to ease the process of composing dynamic expressions. This new API integrates seamlessly with the surql template tag, allowing you to insert param-safe expressions anywhere.

const checkActive = true;

// Query method
await db.select(userTable).where(eq("active", checkActive));

// Custom query
await db.query(surql`SELECT * FROM user WHERE ${eq("active", checkActive)}`);

// Expressions even allow raw insertion
await db.query(surql`SELECT * FROM user ${raw("WHERE active = true")}`);

You can also parse expressions into a string manually using the expr() function

const result: BoundQuery = expr(
    or(
        eq("foo", "bar"),
        false && eq("hello", "world"),
        eq("alpha", "beta"),
        and(
            inside("hello", ["hello"]),
            between("number", 1, 10)
        )
    )
);

βš™οΈ Separation of concerns

The SDK has been rebuilt in a way which allows for optimal code-reuse while still allowing flexibility for future RPC protocol iterations. This is done by dividing the internal SDK logic into three distinct layers.

Engines

Much like in in the original SDK, engines allow you to connect to a specific datastore, whether it be over HTTP or WS, or embedded through @surrealdb/wasm. However, this has now been streamlined further so that engines are only exclusively responsible for communicating and delivering RPC messages to a specific datastore, while implementing the new SurrealDB Protocol pattern.

Controller

The connection controller is responsible for tracking the local connection state and synchronising it with the SurrealDB instance through the instantiated engine implementation. This includes tracking the selected namespace and database, handling authentication, and performing version checks.

Surreal

Much like in the original SDK the Surreal class represents the public API and exposes all necessary public functionality, while wrapping the underlying connection controller.

πŸ“š Updated documentation

TypeScript signatures and documentations have been fixed and updated to correctly describe different arguments and overloads.

Release v2.0.0-alpha.4

25 Jun 10:26
338fd83

Choose a tag to compare

Pre-release

Changes since the previous version

  • Implemented a new query builder pattern for RPC calls
  • Fix WebSocket engine on Chrome (#436)

Full changelog

πŸ“¦ Modularised packages

The repository has been restructured as a monorepo, allowing the publishing of modularised packages. This includes extracting the cbor encoding and decoding logic into a dedicated standalone @surrealdb/cbor package.

πŸ“§ Official event listeners

The original SDK allowed for the listening of events through the leaked internal surreal.emitter field. Instead, the updated SDK provides a type-safe surreal.subscribe() function allowing you to listen to events. Invoking .subscribe() now also returns a cleanup function, which unsubscribes the listener when called.

Example

// Subscribe to events
const unsub = surreal.subscribe("connected", () => {
    ...
});

// Unsubscribe
unsub();

🏹 Access internal state

Additional getters have been added to retrieve internal state from the Surreal instance, such as

  • surreal.namespace and surreal.database to obtain the selected NS and DB
  • surreal.params to obtain defined connection params
  • surreal.accesToken and surreal.refreshToken to obtain authentication tokens

Example

await surreal.use({ namespace: surreal.namespace, database: "other-db" });

πŸ”„ Automatic token refreshing

SurrealDB will now automatically use the provided authentication values when reconnecting and renewing access tokens. In addition, you can now also provide a callable function to resolve your authentication details on the fly, such as when loading tokens from browser storage.

Access token renewal is now also managed by default, meaning the SDK will automatically request a fresh access token using the configured authentication details once the previous token is set to expire. If necessary, you can even pass a custom renewal hook in order to customize this process.

Example

const surreal = new Surreal();

await surreal.connect("http://example.com", {
    namespace: "test",
    database: "test",
    renewAccess: true, // default true
    authentication: () => ({
        username: "foo",
        password: "bar",
    })
});

πŸ“£ Redesigned live query API

The live query functions provided by the Surreal class have been redesigned to feel more intuitive and natural to use. Additionally, live select queries can now be automatically restarted once the driver reconnects.

The record ID will now also be provided as third argument to your handlers, allowing you to determine the record when listening to patch updates.

Example

// Construct a new live subscription
const live = await surreal.live(new Table("users"));

// Listen to changes
live.subscribe((action, result, record) => {
     ...
});

// Kill the query and stop listening
live.kill();

// Create an unmanaged query from an existing id
const [id] = await surreal.query("LIVE SELECT * FROM users");
const live = await surreal.liveOf(id);

❗ Improved parameter explicitness

Query functions now no longer accept strings as table names. Instead, you must explicitly use the Table class to represent tables. This avoids situations where record ids may be accidentally passed as table names, resulting in confusing results.

Example

// tables.ts
const usersTable = new Table("users");
const productsTable = new Table("products");
...

// main.ts
await surreal.select(usersTable);

πŸ”§ Query builder pattern

In order to provide a more transparent and ergonomic way to configure individual RPC calls, a new builder pattern has been introduced allowing the optional chaining of functions on RPC calls.

Example

// Raw queries
const [result] = await db.query("...").raw();

// Serializable results
const record = await db.select(id).jsonify();

βš™οΈ Separation of concerns

The SDK has been rebuilt in a way which allows for optimal code-reuse while still allowing flexibility for future RPC protocol iterations. This is done by dividing the internal SDK logic into three distinct layers.

Engines

Much like in in the original SDK, engines allow you to connect to a specific datastore, whether it be over HTTP or WS, or embedded through @surrealdb/wasm. However, this has now been streamlined further so that engines are only exclusively responsible for communicating and delivering RPC messages to a specific datastore.

Controller

The connection controller is responsible for tracking the local connection state and synchronising it with the SurrealDB instance through the instantiated engine implementation. This includes tracking the selected namespace and database, handling authentication, and performing version checks.

Surreal

Much like in the original SDK the Surreal class represents the public API and exposes all necessary public functionality, while wrapping the underlying connection controller. Each supported version of the RPC protocol receives a dedicated class (e.g. SurrealV1, SurrealV2), with the recommended version being aliases to the original Surreal export.

πŸ“š Updated documentation

TypeScript signatures and documentations have been fixed and updated to correctly describe different arguments and overloads.

Release v2.0.0-alpha.3

03 Jun 10:06
b46c71a

Choose a tag to compare

Pre-release

πŸ“¦ Modularised packages

The repository has been restructured as a monorepo, allowing the publishing of modularised packages. This includes extracting the cbor encoding and decoding logic into a dedicated standalone @surrealdb/cbor package.

πŸ“§ Official event listeners

The original SDK allowed for the listening of events through the leaked internal surreal.emitter field. Instead, the updated SDK provides a type-safe surreal.subscribe() function allowing you to listen to events. Invoking .subscribe() now also returns a cleanup function, which unsubscribes the listener when called.

Example

// Subscribe to events
const unsub = surreal.subscribe("connected", () => {
    ...
});

// Unsubscribe
unsub();

🏹 Access internal state

Additional getters have been added to retrieve internal state from the Surreal instance, such as

  • surreal.namespace and surreal.database to obtain the selected NS and DB
  • surreal.params to obtain defined connection params
  • surreal.accesToken and surreal.refreshToken to obtain authentication tokens

Example

await surreal.use({ namespace: surreal.namespace, database: "other-db" });

πŸ”„ Automatic token refreshing

SurrealDB will now automatically use the provided authentication values when reconnecting and renewing access tokens. In addition, you can now also provide a callable function to resolve your authentication details on the fly, such as when loading tokens from browser storage.

Access token renewal is now also managed by default, meaning the SDK will automatically request a fresh access token using the configured authentication details once the previous token is set to expire. If necessary, you can even pass a custom renewal hook in order to customize this process.

Example

const surreal = new Surreal();

await surreal.connect("http://example.com", {
    namespace: "test",
    database: "test",
    renewAccess: true, // default true
    authentication: () => ({
        username: "foo",
        password: "bar",
    })
});

πŸ“£ Redesigned live query API

The live query functions provided by the Surreal class have been redesigned to feel more intuitive and natural to use. Additionally, live select queries can now be automatically restarted once the driver reconnects.

The record ID will now also be provided as third argument to your handlers, allowing you to determine the record when listening to patch updates.

Example

// Construct a new live subscription
const live = await surreal.live(new Table("users"));

// Listen to changes
live.subscribe((action, result, record) => {
     ...
});

// Kill the query and stop listening
live.kill();

// Create an unmanaged query from an existing id
const [id] = await surreal.query("LIVE SELECT * FROM users");
const live = await surreal.liveOf(id);

❗ Improved parameter explicitness

Query functions now no longer accept strings as table names. Instead, you must explicitly use the Table class to represent tables. This avoids situations where record ids may be accidentally passed as table names, resulting in confusing results.

Example

// tables.ts
const usersTable = new Table("users");
const productsTable = new Table("products");
...

// main.ts
await surreal.select(usersTable);

βš™οΈ Separation of concerns

The SDK has been rebuilt in a way which allows for optimal code-reuse while still allowing flexibility for future RPC protocol iterations. This is done by dividing the internal SDK logic into three distinct layers.

Engines

Much like in in the original SDK, engines allow you to connect to a specific datastore, whether it be over HTTP or WS, or embedded through @surrealdb/wasm. However, this has now been streamlined further so that engines are only exclusively responsible for communicating and delivering RPC messages to a specific datastore.

Controller

The connection controller is responsible for tracking the local connection state and synchronising it with the SurrealDB instance through the instantiated engine implementation. This includes tracking the selected namespace and database, handling authentication, and performing version checks.

Surreal

Much like in the original SDK the Surreal class represents the public API and exposes all necessary public functionality, while wrapping the underlying connection controller. Each supported version of the RPC protocol receives a dedicated class (e.g. SurrealV1, SurrealV2), with the recommended version being aliases to the original Surreal export.

πŸ“š Updated documentation

TypeScript signatures and documentations have been fixed and updated to correctly describe different arguments and overloads.

What's Changed

Full Changelog: v2.0.0-alpha.2...v2.0.0-alpha.3

Release v2.0.0-alpha.2

03 Jun 09:33
3e4d976

Choose a tag to compare

Pre-release

πŸ“¦ Modularised packages

The repository has been restructured as a monorepo, allowing the publishing of modularised packages. This includes extracting the cbor encoding and decoding logic into a dedicated standalone @surrealdb/cbor package.

πŸ“§ Official event listeners

The original SDK allowed for the listening of events through the leaked internal surreal.emitter field. Instead, the updated SDK provides a type-safe surreal.subscribe() function allowing you to listen to events. Invoking .subscribe() now also returns a cleanup function, which unsubscribes the listener when called.

Example

// Subscribe to events
const unsub = surreal.subscribe("connected", () => {
    ...
});

// Unsubscribe
unsub();

🏹 Access internal state

Additional getters have been added to retrieve internal state from the Surreal instance, such as

  • surreal.namespace and surreal.database to obtain the selected NS and DB
  • surreal.params to obtain defined connection params
  • surreal.accesToken and surreal.refreshToken to obtain authentication tokens

Example

await surreal.use({ namespace: surreal.namespace, database: "other-db" });

πŸ”„ Automatic token refreshing

SurrealDB will now automatically use the provided authentication values when reconnecting and renewing access tokens. In addition, you can now also provide a callable function to resolve your authentication details on the fly, such as when loading tokens from browser storage.

Access token renewal is now also managed by default, meaning the SDK will automatically request a fresh access token using the configured authentication details once the previous token is set to expire. If necessary, you can even pass a custom renewal hook in order to customize this process.

Example

const surreal = new Surreal();

await surreal.connect("http://example.com", {
    namespace: "test",
    database: "test",
    renewAccess: true, // default true
    authentication: () => ({
        username: "foo",
        password: "bar",
    })
});

πŸ“£ Redesigned live query API

The live query functions provided by the Surreal class have been redesigned to feel more intuitive and natural to use. Additionally, live select queries can now be automatically restarted once the driver reconnects.

The record ID will now also be provided as third argument to your handlers, allowing you to determine the record when listening to patch updates.

Example

// Construct a new live subscription
const live = await surreal.live(new Table("users"));

// Listen to changes
live.subscribe((action, result, record) => {
     ...
});

// Kill the query and stop listening
live.kill();

// Create an unmanaged query from an existing id
const [id] = await surreal.query("LIVE SELECT * FROM users");
const live = await surreal.liveOf(id);

❗ Improved parameter explicitness

Query functions now no longer accept strings as table names. Instead, you must explicitly use the Table class to represent tables. This avoids situations where record ids may be accidentally passed as table names, resulting in confusing results.

Example

// tables.ts
const usersTable = new Table("users");
const productsTable = new Table("products");
...

// main.ts
await surreal.select(usersTable);

βš™οΈ Separation of concerns

The SDK has been rebuilt in a way which allows for optimal code-reuse while still allowing flexibility for future RPC protocol iterations. This is done by dividing the internal SDK logic into three distinct layers.

Engines

Much like in in the original SDK, engines allow you to connect to a specific datastore, whether it be over HTTP or WS, or embedded through @surrealdb/wasm. However, this has now been streamlined further so that engines are only exclusively responsible for communicating and delivering RPC messages to a specific datastore.

Controller

The connection controller is responsible for tracking the local connection state and synchronising it with the SurrealDB instance through the instantiated engine implementation. This includes tracking the selected namespace and database, handling authentication, and performing version checks.

Surreal

Much like in the original SDK the Surreal class represents the public API and exposes all necessary public functionality, while wrapping the underlying connection controller. Each supported version of the RPC protocol receives a dedicated class (e.g. SurrealV1, SurrealV2), with the recommended version being aliases to the original Surreal export.

πŸ“š Updated documentation

TypeScript signatures and documentations have been fixed and updated to correctly describe different arguments and overloads.

What's Changed

Full Changelog: v2.0.0-alpha.1...v2.0.0-alpha.2