Docs
View as markdown

Node.js API

The @relate/relate package's exports, classes and methods with their TypeScript signatures, open options, value mapping in both directions, errors, and limits.

node

The relate Node.js client A language package that runs the relate engine inside a program's process: the Python, Node.js, Rust, Java, Go and Swift clients. Read more in the glossary is the @relate/relate package: it loads the engine as a native library (librelate_ffi, the C ABI version 2) and runs it in the calling process against a store A relate database: a directory, a single .rl file, or :memory:, opened with --database. Read more in the glossary. query() is synchronous and queryAsync() returns a Promise of the same result. The package ships TypeScript types in index.d.ts; the signatures on this page are read from those types and the package source, and checked against @relate/relate 0.1.2 on Node.js 26; the package requires Node.js 18 or later.

Synopsis

const { Database } = require("@relate/relate");

const db = Database.open(uri, { readOnly: false });   // a Database
const conn = db.connect(namespace);                   // a Connection; namespace defaults to "neo4j"
const result = conn.query(cypher, params);            // a Result holding every row
const later = await conn.queryAsync(cypher, params);  // a Promise of a Result
conn.begin();
const bookmark = conn.commit();                       // "relate:<n>", or null
db.checkpoint();

Exports

ExportKindWhat it is
DatabaseclassAn open store
ConnectionclassRuns statements in one session namespace
Result, ResultSummary, ResultGraphclassesWhat a statement returns
RelateErrorclassThe error every engine failure throws
ResultNotSingleErrorclassThrown by result.single()
CypherFloat, float(value)class, functionA number bound as FLOAT
CypherDate, CypherTime, CypherLocalTime, CypherDateTime, CypherLocalDateTime, CypherDuration, CypherPoint, CypherVectorclassesTyped values for parameters and results

The types file also exports the TypeScript types QueryType, Namespace, Int64, NodeValue, RelationshipValue, PathValue, Row, Stats, Notification, PlanDocument, PlanNode, PlanStartReason, PlanActual and ParamValue.

Loading

require("@relate/relate") finds the library in this order: the path in RELATE_LIB; the platform package @relate/<platform>-<arch> that npm installs as an optional dependency (darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-x64); then a source checkout's build directories. It then checks the library's ABI version.

Failure at require timeError message
No library foundlibrelate_ffi not found; looked in (1) the RELATE_LIB environment variable, (2) the platform package @relate/<platform>-<arch> (lib/ directory), (3) the repo target/debug and target/release directories. Set RELATE_LIB to the library path.
A library with another ABI versionincompatible librelate_ffi: ABI version <n>; this client requires 2
A library without the version symbolincompatible librelate_ffi: no relate_abi_version symbol (the library predates the ABI version symbol); this client requires ABI v2

Database

MemberReturnsBehavior
Database.open(uri: string, options?: { readOnly?: boolean })DatabaseOpens a store; see Open options
db.connect(namespace?: string)ConnectionA new connection; "neo4j" (default), "system" or "system_ontologies", in any case
db.checkpoint()voidWrites the current state to the snapshot

There is no close method. Each statement is durable when query() returns; the store stays open until the process exits.

Open options

OptionDefaultAccepted values
urirequired":memory:"; a directory path (a directory store); a path ending in .rl (a single-file store). A missing store is created, unless readOnly is set
readOnlyfalsetrue loads the store once and refuses writes and checkpoints; a missing store throws
Environment variableEffect
RELATE_LIBThe library path, checked first at require time
RELATE_SNAPSHOT_FORMATThe codec of a new, empty directory store: parquet (default), json or markdown; an existing store keeps its own
RELATE_LOAD_CSV_DIRThe directory local LOAD CSV may read

A file-backed store has one writing open at a time. A directory store also opens readOnly: true beside its writer; a .rl store does not. A failed open throws a RelateError whose message is internal error, with stage "internal", gqlStatus 42000 and code Neo.DatabaseError.General.UnknownError; the message does not say whether the store was locked or missing.

Connection

MemberReturnsBehavior
query(cypher: string, params?: { [name: string]: ParamValue })ResultRuns one statement; auto-commit unless begin() is open
query`…${value}…`ResultThe tagged-template form: each interpolated value becomes a parameter $p0, $p1, …; the same primitive value twice maps to one parameter
queryAsync(cypher, params?), queryAsync`…`Promise<Result>The same, on a worker thread; calls on one connection run one at a time, in order
begin()voidOpens an explicit transaction; a second begin() throws semantic error: already in a transaction
commit()string | nullCommits and returns the bookmark, relate:<n>; null when the transaction wrote nothing. Without a transaction: semantic error: no transaction
rollback()voidDiscards the transaction. Without one: semantic error: no transaction
setNamespace(name: string)voidChanges the session namespace between transactions; inside one it throws a message ending Cannot change the namespace while a transaction is open.

While a queryAsync() is in flight, query, begin, commit, rollback and setNamespace on the same connection throw RelateError: a synchronous <call> cannot start while an async query is in flight on this connection, with code, gqlStatus and stage null. A synchronous call in the same tick as queryAsync() runs first, because the async statement starts on the next microtask.

Result

A Result holds every row from the moment query() returns.

MemberTypeMeaning
columnsstring[]Column names in RETURN order
rowsRow[]Rows as objects keyed by column name
statsStatsThe 11 mutation counters, zeros included: nodes_created, nodes_deleted, relationships_created, relationships_deleted, properties_set, labels_added, labels_removed, indexes_added, indexes_removed, constraints_added, constraints_removed
queryType"r" | "w" | "rw" | "s"A read, a write with no columns, a write that returns columns, a schema statement; EXPLAIN and PROFILE report the wrapped statement's type
namespace"neo4j" | "system" | "system_ontologies"The namespace the statement ran in: its USE target, else the session namespace
bookmarkstring | nullAn auto-commit write's bookmark, relate:<n>; null for a read, an EXPLAIN, a write whose counters are all zero (it changed nothing), and any statement inside an explicit transaction
notificationsNotification[][] on a plain run; filled by EXPLAIN and PROFILE. Keys: code, gql_status, severity (INFORMATION or WARNING), category (PERFORMANCE, UNRECOGNIZED, DEPRECATION, SCHEMA or HINT), title, description, remedy, and operator_id on operator-scoped kinds
planPlanDocument | nullnull on a plain run; for EXPLAIN and PROFILE, a relate.plan/v1 document with schema, profiled, statement and root
single()RowThe one row; zero or several throw ResultNotSingleError: expected exactly one row, got <n>, with rowCount
consume()ResultSummaryThe summary; repeated calls return the same object
graph()ResultGraphThe distinct nodes and relationships in the rows, nested values included
ResultSummary fieldTypeMeaning
countersStatsAs result.stats
namespace, queryType, bookmark, notifications, planas on ResultAs on Result
elapsedMsnumberEngine time for parse, plan and run, in milliseconds
parseMs, planMs, executeMsnumberThe three phases of elapsedMs; their sum is at most elapsedMs
ResultGraph memberTypeMeaning
nodesNodeValue[]Distinct nodes, in the order the rows first carry them, by element id
relationshipsRelationshipValue[]Distinct relationships, in the same order
sizenumberNodes plus relationships

Value mapping

Each result value carries its own type; there is no per-column type list.

Cypher resultJavaScript value
NULL, BOOLEAN, STRINGnull, boolean, string (interior \u0000 kept)
INTEGERnumber within ±(2^53 − 1), bigint beyond it, at every depth
FLOATnumber; NaN and ±Infinity as themselves
NODE{ kind: "node", elementId, id, labels, properties }
RELATIONSHIP{ kind: "relationship", elementId, id, type, startElementId, endElementId, startId, endId, properties }
PATHAn array alternating node, relationship, …, node
LIST, MAPAn array; a plain object in the engine's key order
DATE, ZONED TIME, LOCAL TIMECypherDate, CypherTime, CypherLocalTime
ZONED DATETIME, LOCAL DATETIMECypherDateTime, CypherLocalDateTime
DURATIONCypherDuration
POINT, VECTORCypherPoint, CypherVector

A node An entity in the graph: it carries zero or more labels and a map of properties, and relationships connect it to other nodes. Read more in the glossary and a relationship A directed, typed connection between two nodes, with properties of its own; every relationship has exactly one type and a start and an end node. Read more in the glossary carry elementId, the string Cypher's elementId() returns (4:relate-neo4j:n0, 5:relate-neo4j:r0), and id, what id() returns. A path An alternating sequence of nodes and relationships that a pattern matched, bound with p = … and read with nodes(p), relationships(p) and length(p). Read more in the glossary is an array of both. Byte arrays arrive as a list of integers 0 to 255, and UUIDs as strings.

JavaScript parameterCypher value
null, boolean, stringNULL, BOOLEAN, STRING
number with an integer valueINTEGER, including 1.0
number with a fraction, NaN, ±InfinityFLOAT
float(n) or new CypherFloat(n)FLOAT, also for a whole number
bigintINTEGER, any signed 64-bit value
An array, a plain objectLIST, MAP, converted at every depth; a cycle throws
CypherDate, CypherTime, CypherLocalTime, CypherDateTime, CypherLocalDateTime, CypherDuration, CypherPoint, CypherVectorThe matching type; a decoded value binds back unchanged

undefined, functions, symbols, Date, Map, Set and other class instances throw a TypeError before the statement runs. Each TypeError the client throws has one of these messages; <path> is the parameter name in quotes, followed by [<i>] or .<key> for a nested value, such as "x"[0]:

TypeError messageThrown for
query params must be a plain objectA params argument that is not a plain object, such as an array
query takes (cypher, params?) — extra argumentsMore than two arguments to query() or queryAsync()
query expects a Cypher string or a tagged templateA first argument that is neither
query params: <path> cannot be a Cypher parameterundefined, a function, or a symbol
query params: <path> must be a plain objectA Date, Map, Set or other class instance
query params: <path> is an integer outside the int64 rangeA bigint outside the signed 64-bit range
query params: <path> is cyclicAn array or object that contains itself
query params: <path> coordinate <i> must be an integer for dtype <dtype>A non-integer coordinate in an INTEGER* CypherVector
query params: <path> coordinate <i> must be a number for dtype <dtype>A non-number coordinate in a FLOAT* CypherVector
ClassConstructor and fields
CypherFloat(value); value
CypherDate(year, month, day)
CypherTime(hour, minute, second, nanosecond, offsetSeconds)
CypherLocalTime(hour, minute, second, nanosecond)
CypherDateTime(year, month, day, hour, minute, second, nanosecond, offsetSeconds, zone?); zone is an IANA name, or null for an offset only
CypherLocalDateTime(year, month, day, hour, minute, second, nanosecond)
CypherDuration(months, days, seconds, nanos); the four components stay apart, so PT36H and P1DT12H differ
CypherPoint(srid, x, y, z?); srid 7203 or 4326 in 2D, 9157 or 4979 in 3D; z is undefined in 2D
CypherVector(dtype, values); dtype is FLOAT16, BFLOAT16, FLOAT32, FLOAT, INTEGER8, INTEGER16, INTEGER32 or INTEGER

Every integer field (the temporal and duration components, the values of an INTEGER* vector) follows the Int64 rule: a number, or a bigint beyond the safe range. Point coordinates, CypherFloat.value and the values of a FLOAT* vector are number.

Errors

ThrownWhenFields
RelateErrorEvery failure the engine reports, from open, connect, query, queryAsync (as a rejection), begin, commit, rollback, setNamespace and checkpointmessage, code (the Neo4j status code), gqlStatus (the GQLSTATUS code), stage ("compile", "runtime" or "internal"), retryable, and isRetriable (the same as retryable)
TypeErrorA wrong argument shape, or a parameter the client cannot convertmessage: one of the TypeError messages, naming the value's path where there is one
ResultNotSingleErrorresult.single() with zero or several rowsmessage, rowCount
ErrorThe library is missing or has another ABI version, at require time; or the library sent a malformed error shape, or a query type or namespace outside the fixed values (ABI contract violated: …)message

retryable is true only when re-running the same work can succeed, such as after a write-write conflict, which can surface on a statement or on commit(). A stage of "internal" marks a failure outside the statement. The errors reference lists every engine message with its codes.

Limits

LimitValueWhen exceeded
Integer parameterA signed 64-bit integerTypeError: query params: <path> is an integer outside the int64 range
Statement text4194304 bytesRelateError: syntax error: query text of <n> bytes exceeds the maximum query size of 4194304 bytes
One value a statement buildsThe engine's expression memory capRelateError: This statement tried to build a single value of <size>. …
Writing opens of a file-backed storeOneRelateError from Database.open(): internal error
Calls in flight per connectionOne; queryAsync() calls queueA synchronous call during an async one throws

Capabilities

FeatureNode.js
In-memory storeA store that lives only in the process.yesDatabase.open(":memory:")
Directory storeA store in a directory on disk.yesDatabase.open(path)
Single-file .rl storeA store in one .rl file.yesDatabase.open("graph.rl")
Read-only openAn open that loads the store once and refuses every write.yesDatabase.open(uri, { readOnly: true })
Explicit transactionsSeveral statements committed or rolled back together.yesbegin(), commit(), rollback()
Parameters$name values passed beside the statement.yesquery(statement, { x: 3 })
Column types, counters and timingsMetadata returned with each result.partialcounters, query type, elapsed and its phase split; no per-column type list (each value carries its type)
NotificationsThe engine's query notifications (for example a Cartesian product), each with a remedy.yesresult.notifications
Arrow outputResults as Apache Arrow.nono Arrow call in the client
Change data captureReading committed changes from a slot.nono change-data-capture call in the client
Full-text searchFull-text indexes and their queries.yesCREATE FULLTEXT INDEX and full-text SEARCH
LOAD CSV from local filesLocal LOAD CSV under a configured directory (denied unless one is set).yesRELATE_LOAD_CSV_DIR
Load a store from a URLOpening a .rl store fetched over https.nono open from a URL in the client
Agent modeThe CLI's agent defaults: the 100-row cap and the piped toon output.noA CLI setting

Each cell is yes, no, partial or unknown, with the call, flag or reason. Checked on 2026-11-09 against relate 0.1.2: runs of the CLI binary and relate serve (Bolt, HTTP and MCP), the WebAssembly build this site ships, and the Python, Node.js, C, Java, Go, Swift and Rust clients, and each surface's upstream reference. The C, Java, Go and Swift clients load the same C ABI library as the Node.js client.

Example

An integer beyond Number.MAX_SAFE_INTEGER arrives as a bigint, and float() keeps a whole number a FLOAT:

const { Database, float } = require("@relate/relate");

const conn = Database.open(":memory:", { readOnly: true }).connect();
const row = conn.query(
  "RETURN 2024 AS since, 9007199254740993 AS big, toString($weight) AS weight, toString($count) AS count",
  { weight: float(1), count: 1 }
).single();
console.log(typeof row.since, row.since);
console.log(typeof row.big, row.big.toString());
console.log(row.weight, row.count);
number 2024
bigint 9007199254740993
1.0 1

A path from the team graph arrives as an array of node and relationship objects:

const { Database } = require("@relate/relate");

const TEAM = `
CREATE (ada:Person {name: 'Ada', role: 'engineer'}),
       (grace:Person {name: 'Grace', role: 'engineer'}),
       (noor:Person {name: 'Noor', role: 'maintainer'}),
       (relate:Project {name: 'relate', language: 'Rust'}),
       (tck:Project {name: 'opencypher-tck', language: 'Gherkin'}),
       (ada)-[:WORKS_ON {since: 2024}]->(relate),
       (grace)-[:WORKS_ON {since: 2023}]->(relate),
       (grace)-[:WORKS_ON {since: 2022}]->(tck),
       (noor)-[:MAINTAINS]->(relate),
       (ada)-[:KNOWS]->(grace)
`;

const conn = Database.open(":memory:").connect();
conn.query(TEAM);
const [ada, knows, grace] = conn.query("MATCH p = (:Person {name: 'Ada'})-[:KNOWS]->(:Person) RETURN p").single().p;
console.log(ada.kind, ada.properties.name, ada.elementId, ada.labels.join(","));
console.log(knows.kind, knows.type, knows.elementId, knows.startElementId, knows.endElementId);
console.log(grace.kind, grace.properties.name);
node Ada 4:relate-neo4j:n0 Person
relationship KNOWS 5:relate-neo4j:r4 4:relate-neo4j:n0 4:relate-neo4j:n1
node Grace

See also