
Surrealdb Js
- 117 installs
- 21 repo stars
- Updated June 16, 2026
- surrealdb/agent-skills
Helps with ai & agent building tasks.
About
surrealdb-js is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- surrealdb-js
- AI & Agent Building
- AI-coding skill
Surrealdb Js by the numbers
- 117 all-time installs (skills.sh)
- +12 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #3,909 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/surrealdb/agent-skills --skill surrealdb-jsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 117 |
|---|---|
| repo stars | ★ 21 |
| Last updated | June 16, 2026 |
| Repository | surrealdb/agent-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
SurrealDB JavaScript SDK
The official SDK (surrealdb on npm) works in Node.js, Deno, Bun, and the browser. It connects to a remote SurrealDB instance over WebSocket/HTTP, or runs an embedded engine in-process. Target the latest stable surrealdb release; install without pinning (npm i surrealdb) unless the user requires a specific version.
Installation
npm i surrealdb
# or: pnpm i surrealdb / yarn add surrealdb / bun add surrealdbConnect & select namespace/database
Always connect, then use a namespace + database, then authenticate. Close the connection when finished.
import { Surreal } from "surrealdb";
const db = new Surreal();
await db.connect("ws://127.0.0.1:8000/rpc");
await db.use({ namespace: "test", database: "test" });
await db.signin({ username: "root", password: "root" });
// ... work ...
await db.close();Run a local server with surreal start -u root -p root rocksdb:mydb (or in-memory with surreal start -u root -p root). To run SurrealDB in-process with no server, see references/embedded.md.
Authentication
// Root / namespace / database users
await db.signin({ username: "root", password: "root" });
await db.signin({ namespace: "test", username: "ns_user", password: "..." });
await db.signin({ namespace: "test", database: "test", username: "db_user", password: "..." });
// Record (scope) access — sign in / sign up against a DEFINE ACCESS method
const token = await db.signin({
namespace: "test",
database: "test",
access: "user", // name of the access method
variables: { email: "a@b.com", pass: "secret" },
});
await db.signup({
namespace: "test",
database: "test",
access: "user",
variables: { email: "a@b.com", pass: "secret" },
});
await db.authenticate(token); // re-auth with a stored JWT
await db.invalidate(); // log out the current session
const me = await db.info(); // info about the authenticated record userCRUD
Pass a table as a string ("person") or a RecordId for a specific record. See references/data-types.md for RecordId, Table, Duration, Decimal, and other value classes.
import { RecordId } from "surrealdb";
interface Person { id: RecordId; name: string; age?: number; }
// Create (random id, or a specific RecordId)
await db.create<Person>("person", { name: "Tobie" });
await db.create<Person>(new RecordId("person", "tobie"), { name: "Tobie" });
// Select all from a table, or a single record
const people = await db.select<Person>("person");
const tobie = await db.select<Person>(new RecordId("person", "tobie"));
// Replace the whole record(s)
await db.update<Person>(new RecordId("person", "tobie"), { name: "Tobie", age: 30 });
// Insert or update (upsert)
await db.upsert<Person>(new RecordId("person", "tobie"), { name: "Tobie" });
// Merge partial data into record(s)
await db.merge<Person>("person", { active: true });
// JSON Patch a record
await db.patch(new RecordId("person", "tobie"), [
{ op: "replace", path: "/name", value: "Tobie M H" },
]);
// Bulk insert
await db.insert<Person>("person", [{ name: "A" }, { name: "B" }]);
// Graph relation: person:tobie ->wrote-> article:surreal
await db.relate(new RecordId("person", "tobie"), "wrote", new RecordId("article", "surreal"));
// Delete record(s)
await db.delete("person");
await db.delete(new RecordId("person", "tobie"));Queries
db.query<T>(sql, vars) runs raw SurrealQL and returns an array with one entry per statement. Always pass user input via bound parameters, never string interpolation.
const [people, count] = await db.query<[Person[], number]>(
`SELECT * FROM person WHERE age > $min;
SELECT count() FROM person GROUP ALL;`,
{ min: 18 },
);
// Bind values and RecordIds as parameters
const [found] = await db.query<[Person[]]>(
"SELECT * FROM person WHERE id = $who",
{ who: new RecordId("person", "tobie") },
);Other helpers: db.let(name, value) / db.unset(name) set session parameters usable as $name in later queries; db.run(fnName, version, args) invokes a defined function; db.queryRaw() returns the unparsed RPC response (status + timing per statement).
Live queries
Subscribe to real-time changes on a table with db.live(). Requires a WebSocket (or embedded) connection — not HTTP. See references/live-queries.md.
const queryUuid = await db.live<Person>("person", (action, result) => {
// action: "CREATE" | "UPDATE" | "DELETE" | "CLOSE"
console.log(action, result);
});
await db.kill(queryUuid); // stop the subscriptionReferences
- references/data-types.md —
RecordId,Table,
Duration, Decimal, Uuid, geometry, and CBOR mapping.
- references/embedded.md — embedded engines via
@surrealdb/node (RocksDB / SurrealKV / in-memory) and @surrealdb/wasm (in-memory / IndexedDB).
- references/live-queries.md —
live,
subscribeLive, and kill patterns.
Resources
SDK Data Types
The SDK exports value classes that map 1:1 to SurrealDB's native types. They serialize to/from the database via CBOR, so prefer them over plain strings or numbers when a value is really a record link, duration, decimal, etc. Import any of them from surrealdb.
import {
RecordId, StringRecordId, RecordIdRange,
Table, Duration, Decimal, Uuid, Future,
GeometryPoint, GeometryLine, GeometryPolygon,
Range, BoundIncluded, BoundExcluded,
} from "surrealdb";Record references
// RecordId("table", id) — id can be a string, number, array, or object
const tobie = new RecordId("person", "tobie"); // person:tobie
const item = new RecordId("item", 42); // item:42
const compound = new RecordId("temp", ["London", "2024-01-01"]);
tobie.tb; // "person"
tobie.id; // "tobie"
tobie.toString(); // "person:tobie"
// Parse a record id that is already in string form
const ref = new StringRecordId("person:tobie");
// A range of record ids: person:alice..=person:tobie
const range = new RecordIdRange(
"person",
new BoundIncluded("alice"),
new BoundIncluded("tobie"),
);Use a RecordId anywhere CRUD methods accept a "thing", and bind it as a query parameter rather than interpolating person:tobie into the SurrealQL string.
Table
Wraps a table name as a distinct type (vs. a plain string used as a value).
const person = new Table("person");
await db.select(person);Numbers, durations, time, ids
// Arbitrary-precision decimal (avoids float rounding)
const price = new Decimal("99.99");
// Duration — accepts a SurrealQL duration string or compact form
const ttl = new Duration("1w2d6h");
// UUID
const id = new Uuid("0193e3a1-0000-7000-8000-000000000000");
// DateTime values round-trip as native JS Date objects
const created = new Date();Geometry
const point = new GeometryPoint([-0.118, 51.509]); // [lng, lat]
const line = new GeometryLine([point, new GeometryPoint([2.349, 48.864])]);
const polygon = new GeometryPolygon([line]);Ranges & deferred values
// Numeric/value range: 1..=10
const r = new Range(new BoundIncluded(1), new BoundIncluded(10));
// Future — a value computed by the database when read
const f = new Future("time::now()");CBOR mapping
The SDK uses CBOR (not JSON) on the wire, which preserves these types exactly:
RecordId⇄ record link,Table⇄ table nameDecimal⇄decimal,Duration⇄duration,Uuid⇄uuid- JS
Date⇄datetime, geometry classes ⇄geometry
When you read records back, these fields are returned as the corresponding class instances — check with instanceof and call .toString() / .toJSON() as needed.
Embedded SurrealDB in JavaScript
Run SurrealDB directly inside your process — no server required. Register an embedded engine on the Surreal instance, then connect to an embedded address instead of a ws:///http:// URL. Combine with createRemoteEngines() if you also want the same instance to be able to connect to remote servers.
Both engine packages are ES modules — useimport, notrequire.
Node.js — @surrealdb/node
npm i surrealdb @surrealdb/nodeimport { Surreal, createRemoteEngines } from "surrealdb";
import { createNodeEngines } from "@surrealdb/node";
const db = new Surreal({
engines: {
...createRemoteEngines(),
...createNodeEngines(),
},
});
// In-memory (nothing persisted)
await db.connect("mem://");
// RocksDB persistence
await db.connect("rocksdb://path/to/db");
// SurrealKV persistence
await db.connect("surrealkv://path/to/db");
await db.use({ namespace: "test", database: "test" });
await db.create("person", { name: "Tobie" });
console.log(await db.select("person"));
await db.close();Browser / Deno — @surrealdb/wasm
npm i surrealdb @surrealdb/wasmimport { Surreal, createRemoteEngines } from "surrealdb";
import { createWasmEngines } from "@surrealdb/wasm";
const db = new Surreal({
engines: {
...createRemoteEngines(),
...createWasmEngines(),
},
});
// In-memory
await db.connect("mem://");
// IndexedDB persistence (browser)
await db.connect("indxdb://my-database");
await db.use({ namespace: "test", database: "test" });Choosing an engine
| Address | Engine | Persistence |
|---|---|---|
mem:// | node / wasm | none (in-memory) |
rocksdb://path | @surrealdb/node | on-disk (RocksDB) |
surrealkv://path | @surrealdb/node | on-disk (SurrealKV) |
indxdb://name | @surrealdb/wasm | browser IndexedDB |
ws:// / wss:// | remote (built-in) | remote server |
http:// / https:// | remote (built-in) | remote server |
The full SDK API (CRUD, query, live queries) is identical across embedded and remote engines — only the connect address and registered engines differ. Live queries work on embedded and WebSocket connections, but not over plain HTTP.
Live Queries
Live queries push changes to the client as records are created, updated, or deleted. They require a WebSocket (ws:// / wss://) or an embedded engine connection — they do not work over plain HTTP.
db.live — subscribe to a table
db.live<T>(table, callback?, diff?) starts a live query on a table and returns a Uuid handle. The callback fires once per change.
import { Surreal, RecordId } from "surrealdb";
interface Person { id: RecordId; name: string; }
const queryUuid = await db.live<Person>("person", (action, result) => {
switch (action) {
case "CREATE": console.log("created", result); break;
case "UPDATE": console.log("updated", result); break;
case "DELETE": console.log("deleted", result); break;
case "CLOSE": console.log("subscription closed"); break;
}
});Pass diff = true to receive JSON Patch diffs instead of full records:
const uuid = await db.live<Person>("person", (action, patches) => {
// `patches` is an array of JSON Patch operations
}, true);db.subscribeLive — attach to an existing live query
When you start a live query inside a raw query (LIVE SELECT ...), the result is a Uuid. Attach a handler to it with subscribeLive. You can register multiple handlers for the same handle.
const [uuid] = await db.query<[string]>(
"LIVE SELECT * FROM person WHERE age >= $min",
{ min: 18 },
);
db.subscribeLive<Person>(uuid, (action, result) => {
console.log(action, result);
});db.kill — stop a live query
Always kill subscriptions you no longer need (and before closing in long-lived apps) to free server resources.
await db.kill(queryUuid);Notes
- Each live query handle is tied to the current connection; reconnecting
requires re-subscribing.
- Permissions apply: a record-authenticated user only receives changes to rows
they are allowed to see.
- Use bound parameters (
$minabove) rather than interpolating values into the
LIVE SELECT string.