# RecordId

Type-safe record identifiers with table name and ID components.

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:**
```ts
import { RecordId } from 'surrealdb';
```

**Source:** [value/record-id.ts](https://github.com/surrealdb/surrealdb.js/blob/main/packages/sdk/src/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)` {#constructor}

Create a new record identifier.

```ts title="Syntax"
new RecordId(table, id)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>table</code> <label label="required" /></td>
            <td><code>Tb | Table&lt;Tb&gt;</code></td>
            <td>The table name, either as a string or a <code>Table</code> instance.</td>
        </tr>
        <tr>
            <td><code>id</code> <label label="required" /></td>
            <td><code>Id</code></td>
            <td>The record ID value (string, number, object, array, or RecordId).</td>
        </tr>
    </tbody>
</table>

#### Examples

```ts
// 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` {#table}

The table name component, returned as a `Table` instance.

**Type:** `Table<Tb>`

```ts
const userId = new RecordId('users', 'john');
console.log(userId.table);      // Table { name: 'users' }
console.log(userId.table.name); // 'users'
```

---

### `id` {#id}

The ID value component.

**Type:** `Id`

```ts
const userId = new RecordId('users', 'john');
console.log(userId.id); // 'john'

const productId = new RecordId('products', 42);
console.log(productId.id); // 42
```

## Instance methods

### `.toString()` {#tostring}

Convert the record ID to its string representation.

```ts title="Syntax"
recordId.toString()
```

#### Returns
`string` - String representation in format `table:id`

#### Examples

```ts
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()` {#tojson}

Serialise the record ID for JSON.

```ts title="Syntax"
recordId.toJSON()
```

#### Returns
`string` - JSON-safe string representation

```ts
const userId = new RecordId('users', 'john');
console.log(JSON.stringify(userId)); // '"users:john"'
```

---

### `.equals(other)` {#equals}

Check if two record IDs are equal.

```ts title="Syntax"
recordId.equals(other)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>other</code> <label label="required" /></td>
            <td><code>unknown</code></td>
            <td>Value to compare.</td>
        </tr>
    </tbody>
</table>

#### Returns
`boolean` - True if equal

```ts
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)); // false
```

## Complete examples

### Basic usage

```ts
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

```ts
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

```ts
// 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.

```ts
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

```ts
// 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

```ts
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

```ts
// Table names are validated automatically
try {
    const invalid = new RecordId('invalid-table!', 'id');
} catch (error) {
    console.error('Invalid table name');
}
```

## RecordIdRange {#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

```ts
new RecordIdRange(table, begin, end)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>table</code> <label label="required" /></td>
            <td><code>Tb | Table&lt;Tb&gt;</code></td>
            <td>The table name, either as a string or a <code>Table</code> instance.</td>
        </tr>
        <tr>
            <td><code>begin</code> <label label="required" /></td>
            <td><code>Bound&lt;Id&gt;</code></td>
            <td>Start of the range. Use <code>BoundIncluded</code>, <code>BoundExcluded</code>, or <code>undefined</code>.</td>
        </tr>
        <tr>
            <td><code>end</code> <label label="required" /></td>
            <td><code>Bound&lt;Id&gt;</code></td>
            <td>End of the range. Use <code>BoundIncluded</code>, <code>BoundExcluded</code>, or <code>undefined</code>.</td>
        </tr>
    </tbody>
</table>

### Examples

```ts
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

```ts
// 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

```ts
// 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

```ts
// 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

```ts
// 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](/docs/reference/javascript/api/values/) - All custom data types
- [Table](/docs/reference/javascript/api/values/table.md) - Table references
- [Query builders](/docs/reference/javascript/api/queries/) - Using RecordId in queries
- [SurrealQL record IDs](/docs/reference/query-language/language-primitives/data-types/record-ids.md) - Database record ID documentation
