The JavaScript SDK provides two ways to execute queries against SurrealDB: raw SurrealQL using the .query() method, and structured query builder methods like .select(), .create(), .update(), and .delete(). Both approaches support type-safe generics, chainable configuration, and multiple result formats.
| Method | Description |
|---|---|
db.query(query, bindings?) | Executes raw SurrealQL statements |
db.select(target) | Selects records from the database |
db.create(target) | Creates new records |
db.insert(target, data) | Inserts one or multiple records |
db.update(target) | Updates existing records |
db.upsert(target) | Inserts or replaces records |
db.delete(target) | Deletes records from the database |
db.relate(from, edge, to) | Creates graph relationships between records |
db.run(name, args?) | Executes a SurrealDB function or SurrealML model |
db.set(key, value) | Defines a parameter on the session |
db.unset(key) | Removes a parameter from the session |
The .query() method executes raw SurrealQL statements against the database. You can pass bindings as a second argument to safely inject variables into the query.
const [users] = await db.query<[User[]]>( 'SELECT * FROM users WHERE age > $min_age', { min_age: 18 } );
When executing multiple statements, each result maps to a position in the generic type parameter.
const [users, posts] = await db.query<[User[], Post[]]>(` SELECT * FROM users; SELECT * FROM posts; `);
You can also pass a bound query for automatic parameterization using the surql template tag.
import { surql } from 'surrealdb'; const minAge = 18; const [users] = await db.query<[User[]]>( surql`SELECT * FROM users WHERE age > ${minAge}` );
The .select() method reads records from the database. You can pass a Table to select all records, a RecordId to select a specific record, or a RecordIdRange to select a range.
import { Table, RecordId } from 'surrealdb'; const allUsers = await db.select(new Table('users')); const user = await db.select(new RecordId('users', 'john'));
Query builder methods return chainable promises, allowing you to configure the query before it executes.
import { gt } from 'surrealdb'; const users = await db.select(new Table('users')) .fields('name', 'email', 'age') .where(gt('age', 18)) .limit(10) .start(0) .fetch('posts');
In the above example, we use the gt() function to create a greater than condition. This function is part of the expression utilities that are available in the SDK. If you prefer to write raw condition, you can use the raw() function to insert the condition directly into the query.
import { raw } from 'surrealdb'; const users = await db.select(new Table('users')) .fields('name', 'email', 'age') .where(raw('age > 18')) .limit(10) .start(0) .fetch('posts');
The .create() method creates new records. Use .content() to set the record data. When passed a Table, SurrealDB generates a random ID. When passed a RecordId, the record is created with that specific ID.
const user = await db.create(new RecordId('users', 'john')) .content({ name: 'John Doe', email: 'john@example.com', }); const autoId = await db.create(new Table('users')) .content({ name: 'Jane Doe' });
The .insert() method inserts one or multiple records at once. This is more efficient than calling .create() in a loop when working with bulk data.
const users = await db.insert(new Table('users'), [ { name: 'Alice', email: 'alice@example.com' }, { name: 'Bob', email: 'bob@example.com' }, ]);
The .update() and .upsert() methods modify existing records. Instead of passing content as a second argument, you choose an update strategy by chaining .content(), .merge(), .replace(), or .patch().
Replace the entire record with new data. Any existing fields not included in the new data will be removed.
await db.update(new RecordId('users', 'john')) .content({ name: 'John Smith', email: 'john.smith@example.com', });
Merge new fields into the existing record. Existing fields that are not specified remain unchanged.
await db.update(new RecordId('users', 'john')) .merge({ email: 'new@example.com' });
Apply JSON Patch operations for fine-grained modifications.
await db.update(new RecordId('users', 'john')) .patch([ { op: 'replace', path: '/email', value: 'new@example.com' }, { op: 'add', path: '/verified', value: true }, ]);
You can also filter which records to update using .where().
await db.update(new Table('users')) .merge({ verified: true }) .where('age >= 18');
The .delete() method removes records from the database. Like other query methods, it accepts a Table, RecordId, or RecordIdRange.
await db.delete(new RecordId('users', 'john')); await db.delete(new Table('users'));
The .relate() method creates edges between records in SurrealDB’s graph model. You specify the source record(s), the edge table, and the target record(s).
await db.relate( new RecordId('users', 'john'), new Table('likes'), new RecordId('posts', '1'), { timestamp: new Date() }, );
The .run() method executes SurrealDB functions or SurrealML models by name. You can pass arguments and optionally specify a model version.
const result = await db.run('fn::calculate_total', [100, 0.2]); const prediction = await db.run('ml::predict', '1.0.0', [inputData]);
You can define parameters on the current session using .set() and remove them with .unset(). Session parameters are available in all subsequent queries as $name variables and persist for the lifetime of the session.
await db.set('current_user', { first: 'Tobie', last: 'Morgan Hitchcock', }); await db.query('CREATE post SET author = $current_user'); await db.unset('current_user');
When running multi-statement queries, you can use .collect() to pick specific result indexes rather than receiving all results.
const [foo, bar] = await db.query(` LET $a = 1; LET $b = 2; SELECT * FROM users; SELECT * FROM posts; `).collect<[User[], Post[]]>(2, 3);
The .stream() method returns an async iterable of response frames, allowing you to process results incrementally. Each frame is either a value, a completion signal with query stats, or an error.
const stream = db.query('SELECT * FROM large_table').stream(); for await (const frame of stream) { if (frame.isValue<User>()) { processUser(frame.value); } else if (frame.isDone()) { console.log('Duration:', frame.stats.duration); } else if (frame.isError()) { console.error(frame.error); } }
NoteStreaming is currently not yet supported by SurrealDB, and this API exists primarily for future server-side record streaming.
The .json() method converts SurrealDB value types in query results to their JSON representations. This is useful when you need to serialize results for APIs or tools that don’t understand SurrealDB’s custom types.
const [products] = await db.query<[Product[]]>('SELECT * FROM product').json();
Learn more about the jsonify utility in the Utilities concept page.
The .responses() method returns the full response objects including success status, query stats, and error information for each statement.
const responses = await db.query('SELECT * FROM users; SELECT * FROM posts').responses(); for (const response of responses) { if (response.success) { console.log('Result:', response.result); console.log('Duration:', response.stats?.duration); } else { console.error('Error:', response.error.message); } }
surql and BoundQuery