The RecordId class provides type-safe record identifiers in SurrealDB. Each record ID consists of a table name and an ID value, represented as table:id in SurrealQL.
Import:
import { RecordId } from 'surrealdb';Source: value/record-id.ts
Type parameters
Tb extends string- The table name (string literal type for type safety)Id- The ID value type (string, number, object, etc.)
Constructor
new RecordId(table, id)
Create a new record identifier.
new RecordId(table, id)Parameters
Parameter | Type | Description |
|---|---|---|
table | Tb | Table<Tb> | The table name, either as a string or a Tableinstance. |
id | Id | The record ID value (string, number, object, array, or RecordId). |
Examples
// String IDs
const user = new RecordId('users', 'john');
const post = new RecordId('posts', '123');
// Numeric IDs
const product = new RecordId('products', 42);
// Complex/structured IDs
const metric = new RecordId('metrics', {
service: 'api',
timestamp: 1234567890
});
// Array IDs
const compound = new RecordId('items', ['type-a', 123]);
// Nested RecordId
const nested = new RecordId('links', new RecordId('users', 'alice'));
// Type-safe with generics
const typedUser = new RecordId<'users', string>('users', 'john');Properties
table
The table name component, returned as a Table instance.
**Type:** `Table`
const userId = new RecordId('users', 'john');
console.log(userId.table); // Table { name: 'users' }
console.log(userId.table.name); // 'users' id
The ID value component.
Type: Id
const userId = new RecordId('users', 'john');
console.log(userId.id); // 'john'
const productId = new RecordId('products', 42);
console.log(productId.id); // 42Instance methods
.toString()
Convert the record ID to its string representation.
recordId.toString()Returns
string - String representation in format table:id
Examples
const userId = new RecordId('users', 'john');
console.log(userId.toString()); // 'users:john'
const productId = new RecordId('products', 42);
console.log(productId.toString()); // 'products:42'
const complex = new RecordId('items', { type: 'widget', id: 5 });
console.log(complex.toString()); // 'items:{ type: "widget", id: 5 }' .toJSON()
Serialize the record ID for JSON.
recordId.toJSON()Returns
string - JSON-safe string representation
const userId = new RecordId('users', 'john');
console.log(JSON.stringify(userId)); // '"users:john"' .equals(other)
Check if two record IDs are equal.
recordId.equals(other)Parameters
Parameter | Type | Description |
|---|---|---|
other | unknown | Value to compare. |
Returns
boolean - True if equal
const a = new RecordId('users', 'john');
const b = new RecordId('users', 'john');
const c = new RecordId('users', 'jane');
console.log(a.equals(b)); // true
console.log(a.equals(c)); // falseComplete examples
Basic usage
import { Surreal, RecordId } from 'surrealdb';
const db = new Surreal();
await db.connect('ws://localhost:8000');
// Create record with specific ID
const user = await db.create(new RecordId('users', 'john'))
.content({
name: 'John Doe',
email: 'john@example.com'
});
// Select by record ID
const retrieved = await db.select(new RecordId('users', 'john'));
// Update by record ID
await db.update(new RecordId('users', 'john'))
.merge({ status: 'active' });
// Delete by record ID
await db.delete(new RecordId('users', 'john'));Type-safe record IDs
interface User {
id: RecordId<'users', string>;
name: string;
email: string;
}
// TypeScript enforces correct table name
const userId: RecordId<'users', string> = new RecordId('users', 'alice');
// This would be a type error:
// const wrong: RecordId<'posts', string> = new RecordId('users', 'alice');
const user = await db.select<User>(userId);Complex ID structures
// Time-series data with structured IDs
const metricId = new RecordId('metrics', {
service: 'api',
host: 'server-01',
timestamp: 1234567890
});
await db.create(metricId).content({
cpu: 45.2,
memory: 78.1
});
// Compound keys
const sessionId = new RecordId('sessions', {
userId: 'john',
deviceId: 'device-123'
});Parsing from strings
Use StringRecordId to pass a record ID string directly without parsing it into table and ID components. The string is sent as-is to SurrealDB.
import { StringRecordId } from 'surrealdb';
// Use a record ID string directly
const userInput = 'users:john';
const userId = new StringRecordId(userInput);
const user = await db.select(userId);
// Use in queries
const recordId = new StringRecordId('users:alice');
const result = await db.select(recordId);Working with relations
// Create relationship using record IDs
const from = new RecordId('users', 'john');
const to = new RecordId('posts', '123');
const edge = await db.relate(
from,
new Table('likes'),
to,
{ timestamp: DateTime.now() }
);
console.log('Edge from:', edge.in); // RecordId('users', 'john')
console.log('Edge to:', edge.out); // RecordId('posts', '123')UUID-based record IDs
import { Uuid } from 'surrealdb';
// Generate time-ordered IDs
const userId = new RecordId('users', Uuid.v7());
// Generate random IDs
const sessionId = new RecordId('sessions', Uuid.v4());
await db.create(userId).content({
name: 'Alice',
created: DateTime.now()
});Validation
// Table names are validated automatically
try {
const invalid = new RecordId('invalid-table!', 'id');
} catch (error) {
console.error('Invalid table name');
}RecordIdRange
The RecordIdRange class represents a range of record IDs for querying multiple records.
Bounds are specified using the BoundIncluded and BoundExcluded classes, or undefined for unbounded:
new BoundIncluded(value)— inclusive bound (>=or<=)new BoundExcluded(value)— exclusive bound (>or<)undefined— unbounded (no limit)
Constructor
new RecordIdRange(table, begin, end)Parameters
Parameter | Type | Description |
|---|---|---|
table | Tb | Table<Tb> | The table name, either as a string or a Tableinstance. |
begin | Bound<Id> | Start of the range. Use BoundIncluded, BoundExcluded, or undefined. |
end | Bound<Id> | End of the range. Use BoundIncluded, BoundExcluded, or undefined. |
Examples
import { RecordIdRange, BoundIncluded, BoundExcluded } from 'surrealdb';
// Select range of users from 'a' (inclusive) to 'f' (exclusive)
const range = new RecordIdRange(
'users',
new BoundIncluded('a'),
new BoundExcluded('f'),
);
const users = await db.select(range);
// Numeric range: 1 (inclusive) to 100 (inclusive)
const numRange = new RecordIdRange(
'items',
new BoundIncluded(1),
new BoundIncluded(100),
);
const items = await db.select(numRange);
// Unbounded start, exclusive end
const upToF = new RecordIdRange(
'users',
undefined,
new BoundExcluded('f'),
);
// Inclusive start, unbounded end
const fromA = new RecordIdRange(
'users',
new BoundIncluded('a'),
undefined,
);
// Delete range
await db.delete(new RecordIdRange(
'logs',
new BoundIncluded('2024-01-01'),
new BoundExcluded('2024-02-01'),
));Best practices
1. Use type parameters
// Good: Type-safe
type UserId = RecordId<'users', string>;
const userId: UserId = new RecordId('users', 'john');
// Better: Enforce at compile time
function getUser(id: RecordId<'users', string>) {
return db.select(id);
}2. Prefer RecordId over strings
// Good: Type-safe with validation
const user = await db.select(new RecordId('users', 'john'));
// Avoid: String-based (no validation)
const user = await db.query('SELECT * FROM users:john').collect();3. Use structured IDs for complex keys
// Good: Structured composite key
const id = new RecordId('events', {
userId: 'john',
timestamp: Date.now()
});
// Avoid: String concatenation
const id = new RecordId('events', `john-${Date.now()}`);4. Handle construction errors
// Good: Safe construction
try {
const id = new RecordId(tableName, idValue);
const record = await db.select(id);
} catch (error) {
console.error('Invalid record ID format');
}See also
Data types overview - All custom data types
Table - Table references
Query builders - Using RecordId in queries
SurrealQL record IDs - Database record ID documentation