Skip to content
NEW BENCHMARKS

SurrealDB 3.x by the numbers

View

1/3

Release 1.0

3 patch releases · Latest 1.0.2 on Dec 21, 2023

Newer release line available: 1.1

Bug fixes: Support connecting to beta servers from the Rust SDK.

1.0.2

Released on Dec 21, 2023

Bug fixes:

  • Support connecting to beta servers from the Rust SDK.

Upgrade or install

Get SurrealDB v1.0.2

Pick how you want to install or upgrade. Surrealist can update connected instances in place, or choose a platform below to copy a CLI command for v1.0.2.

You can upgrade your SurrealDB Cloud instance to v1.0.2 effortlessly through the Surrealist app.

  1. Select your organisation and instance
  2. On the dashboard, click on the "Upgrade" button
  3. Your instance will be updated and restarted automatically

1.0.1

Released on Dec 14, 2023

Bug fixes:

  • Add a patch for GHSA-x5fr-7hhj-34j3.

  • Tables defined without explicit permissions have NONE instead of FULL permissions.

  • Table permissions are always explicitly displayed with the INFO FOR DB statement.

Newer patch available

Upgrade to 1.0.2

You are viewing the 1.0.1 changelog. A newer patch in this release line is available - we recommend running 1.0.2 for the latest fixes and improvements.

View 1.0.2 release notes

1.0.0

Released on Sep 13, 2023

After numerous beta releases crammed into just months of development, we are releasing SurrealDB v1.0.0! 🎉

Here follow some of our 1.0.0 highlights:

v1.0.0 introduces new type validation methods. These new methods allow you to check which type any sort of value is on the go.

LET $value = "I am a string";

RETURN [
type::is::string($value),
type::is::record($value),
type::is::number($value),
];

It was previously difficult to select, create, update or delete just a single record, so we simplified it a bit.

// Will return an object, as we can guarantee just a single record is requested
SELECT * FROM ONLY person:tobie;

// This will throw an error, as multiple records are being created and returned
CREATE ONLY person:tobie, person:jaime;

v1.0.0 introduces Live Queries. This powerful technology allows you to write applications where you can serve realtime updates to your frontend.

// You can initiate the live query in your application
LET $lq = LIVE SELECT * FROM person WHERE age > 18;
// And you can dispose the live query once it is no longer needed
KILL $lq;
// SurrealDB SDKs allow you to easily initiate live queries, and to process the received messages.
const lq = await db.live('person', function ({ action, result }) {
if (action == 'CREATE') {
renderPersonInFrontend(result);
}
});

// Once you no longer need the live feed of updates, you can dispose the live query
await db.kill(lq);

With Full Text Search, you can efficiently store and index data, and search through it.

// We can define an analyzer on our database,
// Then, define an index to dictate which content we want to index (yep, that easy)
DEFINE ANALYZER simple TOKENIZERS blank,class FILTERS snowball(english);
DEFINE INDEX content ON article FIELDS content SEARCH ANALYZER simple BM25 HIGHLIGHTS;

// We can then create some content
CREATE article SET content = "Join us at SurrealDB World, as we unveil our version 1.0.0 to the world!";
CREATE article SET content = "We will absolutely be at Surreal World!";

// And lastly, select some data from the article table.
// We use the special "@num@" operator, where we define a reference.
// We can then reference back in the search::* functions.
SELECT
*,
search::score(1) AS score,
search::offsets(1) AS offsets,
search::highlight('<b>', '</b>', 1) AS highlighted
FROM article WHERE
content @1@ 'world'
ORDER BY score DESC;

All capabilities are disabled by default. This means that by default, you are not able to use any methods, embedded scripting functions, make outbound network calls, or access the database anonymously. Down below follows a set of examples to showcase how one can configure capabilities.

Capabilities are further documented in the Capabilities documentation.

# Allow all capabilities {#1-0-0-allow-all-capabilities}
user@localhost % surreal start --allow-all

# Allow all functions, except for custom functions {#1-0-0-allow-all-functions-except-for-custom-functions}
user@localhost % surreal start --allow-funcs --deny-funcs fn

# Allow all capabilities, but deny guest/anonymous access to the database {#1-0-0-allow-all-capabilities-but-deny-guestanonymous-access-to-the-database}
user@localhost % surreal start --allow-all --deny-guests

It is now possible to define multiple root users in SurrealDB. This change did require some changes in the way that you start your database, however.

With this change, you will now only initially have to provide the --user and --pass flags to create the initial root user, but once the first root user exists, they will no longer be utilised.

For more information, check out the Authentication guide, and the surreal start and DEFINE USER documentation.

# When you initially start the database, you can create the first root user. {#1-0-0-when-you-initially-start-the-database-you-can-create-the-first-root-user}
user@localhost % surreal start --auth --user root --pass root file:database.db

# In the future, you don't need to specify the --user and --pass flags anymore {#1-0-0-in-the-future-you-dont-need-to-specify-the---user-and---pass-flags-anymore}
user@localhost % surreal start --auth file:database.db
// Afterwards, you are able to create multiple root users.
DEFINE USER tobie ON ROOT PASSWORD "SecurePassword!" ROLES OWNER;
DEFINE USER jaime ON ROOT PASSWORD "SecurePassword!" ROLES EDITOR;
DEFINE USER john ON ROOT PASSWORD "SecurePassword!" ROLES VIEWER;

v1.0.0 introduces a more strict and powerful typing system. It makes things more simple to understand, and it goes a long way in preventing all kinds of weird bugs in your schemas!

// Where you previously had to manually assert that a field does not contain NONE or NULL
DEFINE FIELD age ON person TYPE number ASSERT $value != NONE AND $value != NULL;

// You can now simply set the type and be ensured that only a number will be stored in that field.
DEFINE FIELD age ON person TYPE number;

// To make types optional, you can use the newly introduced "option<type>" type.
// This guarantees the field to be either empty (NONE), or a number.
DEFINE FIELD age ON person TYPE option<number>;

// We also introduces some new types!
DEFINE FIELD ratings ON movie TYPE array<number>;
DEFINE FIELD best_ratings ON movie TYPE array<number, 5>;
DEFINE FIELD unique_ratings ON movie TYPE set<number, 5>;

// And we made sure that the record type is also in-line with this new format
DEFINE FIELD author ON book TYPE record<person>;

// Lastly, it is now possible to set a union type.
DEFINE FIELD title ON article TYPE string | number;
// They can also be used in "nested" types
DEFINE FIELD description ON article TYPE option<string | number>;
DEFINE FIELD author ON article TYPE record<person | admin>;
// You previously needed to involve logic with the "VALUE" clause to set a default value.
DEFINE FIELD enabled ON user TYPE bool VALUE $value OR $before OR true;
// You can now simply use the DEFAULT clause for this.
DEFINE FIELD enabled ON user TYPE bool DEFAULT true;

// As an added bonus, when you only define a VALUE clause with a simple value,
// then that will also act as the default value.
DEFINE FIELD constant ON demo VALUE 123;
// This will act the same as the following
DEFINE FIELD constant ON demo VALUE 123 DEFAULT 123;

Scope and anonymous users previously had access to every defined global parameter and function. You can now define these resources with a PERMISSIONS clause to protect them.

DEFINE PARAM $perms_none VALUE 'Nobody can see me' PERMISSIONS NONE;
DEFINE PARAM $perms_full VALUE 'Everybody can see me' PERMISSIONS FULL;
DEFINE PARAM $perms_scope VALUE 'Only admins can see me' PERMISSIONS WHERE $scope = 'admin';

DEFINE FUNCTION fn::perms::none() {
RETURN 'Nobody can invoke me';
} PERMISSIONS NONE;

DEFINE FUNCTION fn::perms::full() {
RETURN 'Everybody can invoke me';
} PERMISSIONS FULL;

DEFINE FUNCTION fn::perms::scope() {
RETURN 'Only admins can invoke me';
} PERMISSIONS WHERE $scope = 'admin';
// Here, we loop through an array with three numbers.
// For any number under two, we skip the iteration
// For any number above two, we break the loop
// The result is that only when the number is two, we will create the "person:two" record.

FOR $num IN [1, 2, 3] {
IF $num < 2 {
CONTINUE;
};

IF $num > 2 {
BREAK;
};

CREATE person:two;
};

// Here, we select the id for every person in the database, and we gift them a ticket to SurrealDB World.
FOR $person IN (SELECT VALUE id FROM person) {
CREATE gift CONTENT {
recipient: $person,
type: "ticket",
event: "SurrealDB World"
};
};

Did something unexpected happen, and do you want to throw an error to the client? Now you can!

LET $message = "Some error message";
THROW "Failed to perform action: " + $message;
// Give extra context about a certain resource
DEFINE TABLE user COMMENT "This table will store users!";

We found the IF ELSE statement to be a bit bulky at times. Now, when you use a block ({}) as the body of the statement, you can skip out on the THEN and END keywords!

// Previously
IF something = true THEN {
RETURN 123;
} END;

// Now
IF something = true {
RETURN 123;
};

With fetch(), query(), value() and basically every SurrealQL function now being available within the embedded scripting functions, they are a very powerful extension to SurrealQL, and can be used to solve complex problems otherwise impossible!

Read more about them in the Embedded scripting functions documentation.

function() {
const page = await fetch('https://google.com');
const people = await surrealdb.query('SELECT * FROM person');
const auth = await surrealdb.value('auth');
const uuid = surrealdb.functions.rand.uuid.v4();
}

SCHEMAFULL and SCHEMALESS functionality can now be used together, suitable for capturing schema-free log data.

// We want to define a SCHEMAFULL table
DEFINE TABLE person SCHEMAFULL;
// But we want one field to allow any content
DEFINE FIELD settings on person TYPE object FLEXIBLE;
// We can then set the field value without a schema
CREATE person:test CONTENT {
settings: {
nested: {
object: {
thing: 'test'
}
}
}
};

It is now possible to run blocks of code, with support for an arbitrary number of statements, including LET and RETURN statements. This allows for writing advanced custom logic, and allowing for more complicated handling of data operations.

DEFINE FIELD average_sales ON metrics VALUE {
LET $sales = (SELECT VALUE quantity FROM sales);
LET $total = math::sum($sales);
LET $count = count($sales);
RETURN ($total / $count);
};

SurrealDB now supports the ability to define global database-wide custom functions, which allow for complicated or repeated user-defined code, to be run seamlessly within any query across the database. Custom functions support typed arguments, and multiple nested queries with custom logic.

-- Define a global function which can be used in any query
DEFINE FUNCTION fn::get_person($first: string, $last: string, $birthday: string) {

LET $person = SELECT * FROM person WHERE [first, last, birthday] = [$first, $last, $birthday];

RETURN IF $person[0].id THEN
$person[0]
ELSE
CREATE person SET first = $first, last = $last, birthday = $birthday
END;

};

-- Call the global custom function, receiving the returned result
LET $person = fn::get_person('Tobie', 'Morgan Hitchcock', '2022-09-21');

Newer patch available

Upgrade to 1.0.2

You are viewing the 1.0.0 changelog. A newer patch in this release line is available - we recommend running 1.0.2 for the latest fixes and improvements.

View 1.0.2 release notes

Our newsletter

Get tutorials, AI agent recipes, webinars, and early product updates in your inbox every two weeks

SurrealDB

The context layer for AI agents.

Documents, graphs, vectors, time-series, and memory.
One transaction, one query, one deployment.

Explore with AI

Independently verified

SOC 2 Type 2

GDPR

Cyber Essentials Plus

ISO 27001

Trust Centre

Copyright © 2026 SurrealDB Ltd. Registered in England and Wales. Company no. 13615201

Registered address: 3rd Floor 1 Ashley Road, Altrincham, Cheshire, WA14 2DT, United Kingdom

Trading address: Huckletree Oxford Circus, 213 Oxford Street, London, W1D 2LG, United Kingdom