# Value types

The SQON library provides custom classes for SurrealDB-specific data types, re-exported by the JavaScript SDK for convenience.

SurrealDB has types that JavaScript does not: record IDs, nanosecond datetimes, arbitrary-precision decimals, and others. They live in [`@surrealdb/sqon`](https://www.npmjs.com/package/@surrealdb/sqon), which also ships codecs and a few core utilities. The `surrealdb` package re-exports all of it, so either import path works:

```ts
// With the driver (usual choice)
import { RecordId, Table, DateTime } from 'surrealdb';

// SQON only (no database client)
import { RecordId, Table, DateTime } from '@surrealdb/sqon';
```

The classes validate input, keep database precision, and plug into query methods and [codecs](/docs/reference/javascript/concepts/codecs.md).

## API references

<table>
	<thead>
		<tr>
			<th scope="col">Class</th>
			<th scope="col">Description</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td scope="row" data-label="Class"><a href="/docs/reference/javascript/api/values/record-id.md"> <code> RecordId </code></a></td>
			<td scope="row" data-label="Description">Type-safe record identifiers with table and ID components</td>
		</tr>
		<tr>
			<td scope="row" data-label="Class"><a href="/docs/reference/javascript/api/values/table.md"> <code> Table </code></a></td>
			<td scope="row" data-label="Description">Type-safe table references for query methods</td>
		</tr>
		<tr>
			<td scope="row" data-label="Class"><a href="/docs/reference/javascript/api/values/datetime.md"> <code> DateTime </code></a></td>
			<td scope="row" data-label="Description">Datetime values with nanosecond precision</td>
		</tr>
		<tr>
			<td scope="row" data-label="Class"><a href="/docs/reference/javascript/api/values/duration.md"> <code> Duration </code></a></td>
			<td scope="row" data-label="Description">Time duration values with multiple unit support</td>
		</tr>
		<tr>
			<td scope="row" data-label="Class"><a href="/docs/reference/javascript/api/values/decimal.md"> <code> Decimal </code></a></td>
			<td scope="row" data-label="Description">Arbitrary precision decimal numbers</td>
		</tr>
		<tr>
			<td scope="row" data-label="Class"><a href="/docs/reference/javascript/api/values/uuid.md"> <code> Uuid </code></a></td>
			<td scope="row" data-label="Description">Universally unique identifiers (v4 and v7)</td>
		</tr>
		<tr>
			<td scope="row" data-label="Class"><a href="/docs/reference/javascript/api/values/range.md"> <code> Range </code></a></td>
			<td scope="row" data-label="Description">Bounded or unbounded range values</td>
		</tr>
		<tr>
			<td scope="row" data-label="Class"><a href="/docs/reference/javascript/api/values/file-ref.md"> <code> FileRef </code></a></td>
			<td scope="row" data-label="Description">References to files stored in SurrealDB</td>
		</tr>
		<tr>
			<td scope="row" data-label="Class"><a href="/docs/reference/javascript/api/values/geometry.md"> <code> Geometry* </code></a></td>
			<td scope="row" data-label="Description">GeoJSON geometry types (Point, Line, Polygon, etc.)</td>
		</tr>
	</tbody>
</table>

## Type mapping

SurrealQL types map to JavaScript types as follows:

<table>
	<thead>
		<tr>
			<th scope="col">SurrealQL type</th>
			<th scope="col">JavaScript type</th>
			<th scope="col">Example</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td><code>bool</code></td>
			<td><code>boolean</code></td>
			<td><code>true</code>, <code>false</code></td>
		</tr>
		<tr>
			<td><code>int</code>, <code>float</code></td>
			<td><code>number</code></td>
			<td><code>42</code>, <code>3.14</code></td>
		</tr>
		<tr>
			<td><code>string</code></td>
			<td><code>string</code></td>
			<td><code>"hello"</code></td>
		</tr>
		<tr>
			<td><code>null</code></td>
			<td><code>null</code></td>
			<td><code>null</code></td>
		</tr>
		<tr>
			<td><code>none</code></td>
			<td><code>undefined</code></td>
			<td><code>undefined</code></td>
		</tr>
		<tr>
			<td><code>array</code></td>
			<td><code>Array</code></td>
			<td><code>[1, 2, 3]</code></td>
		</tr>
		<tr>
			<td><code>object</code></td>
			<td><code>Object</code></td>
			<td><code>{`{ key: "value" }`}</code></td>
		</tr>
		<tr>
			<td><code>set</code></td>
			<td><code>Set</code></td>
			<td><code>new Set([1, 2, 3])</code></td>
		</tr>
		<tr>
			<td><code>bytes</code></td>
			<td><code>Uint8Array</code></td>
			<td><code>new Uint8Array([...])</code></td>
		</tr>
		<tr>
			<td><code>record</code></td>
			<td><a href="/docs/reference/javascript/api/values/record-id.md"><code>RecordId</code></a></td>
			<td><code>new RecordId('users', 'john')</code></td>
		</tr>
		<tr>
			<td>-</td>
			<td><a href="/docs/reference/javascript/api/values/table.md"><code>Table</code></a></td>
			<td><code>new Table('users')</code></td>
		</tr>
		<tr>
			<td><code>datetime</code></td>
			<td><a href="/docs/reference/javascript/api/values/datetime.md"><code>DateTime</code></a></td>
			<td><code>DateTime.now()</code></td>
		</tr>
		<tr>
			<td><code>duration</code></td>
			<td><a href="/docs/reference/javascript/api/values/duration.md"><code>Duration</code></a></td>
			<td><code>Duration.parse('1h30m')</code></td>
		</tr>
		<tr>
			<td><code>decimal</code></td>
			<td><a href="/docs/reference/javascript/api/values/decimal.md"><code>Decimal</code></a></td>
			<td><code>new Decimal('19.99')</code></td>
		</tr>
		<tr>
			<td><code>uuid</code></td>
			<td><a href="/docs/reference/javascript/api/values/uuid.md"><code>Uuid</code></a></td>
			<td><code>Uuid.v7()</code></td>
		</tr>
		<tr>
			<td><code>geometry</code></td>
			<td><a href="/docs/reference/javascript/api/values/geometry.md"><code>Geometry*</code></a></td>
			<td><code>new GeometryPoint([1, 2])</code></td>
		</tr>
		<tr>
			<td><code>range</code></td>
			<td><a href="/docs/reference/javascript/api/values/range.md"><code>Range</code></a></td>
			<td><code>new Range(1, 10)</code></td>
		</tr>
		<tr>
			<td><code>file</code></td>
			<td><a href="/docs/reference/javascript/api/values/file-ref.md"><code>FileRef</code></a></td>
			<td><code>record.avatar</code></td>
		</tr>
	</tbody>
</table>

## RecordId and Table

A [`RecordId`](/docs/reference/javascript/api/values/record-id.md) is a table name plus an ID. A [`Table`](/docs/reference/javascript/api/values/table.md) is a table reference on its own. From v2 onward, query methods expect a `Table` instance for table names, not a bare string, so table names are not confused with record IDs.

```ts
import { RecordId, Table } from 'surrealdb';

const userId = new RecordId('users', 'john');
const user = await db.select(userId);

const usersTable = new Table('users');
const allUsers = await db.select(usersTable);

const parsed = RecordId.parse('users:john');
```

The ID component of a `RecordId` can be a `string`, `number`, `bigint`, `Uuid`, array, or object. The `Table` class also supports a type parameter for type-safe query results.

```ts
const users = new Table<User>('users');
const results: User[] = await db.select(users);
```

## DateTime and Duration

A [`DateTime`](/docs/reference/javascript/api/values/datetime.md) is a timestamp with nanosecond precision. JavaScript's `Date` stops at milliseconds, so `DateTime` keeps what SurrealDB actually stored. A [`Duration`](/docs/reference/javascript/api/values/duration.md) follows SurrealQL duration syntax.

```ts
import { DateTime, Duration } from 'surrealdb';

const now = DateTime.now();
const parsed = DateTime.parse('2024-01-15T12:00:00.123456789Z');
const jsDate = now.toDate();
const iso = now.toString();

const duration = Duration.parse('1h30m45s');
const ms = duration.toMilliseconds();
const seconds = duration.toSeconds();
```

## Decimal

A [`Decimal`](/docs/reference/javascript/api/values/decimal.md) holds a decimal without floating-point rounding. Construct it from a string when precision matters.

```ts
import { Decimal } from 'surrealdb';

const price = new Decimal('19.99');
const display = price.toString();
const number = price.toNumber();
```

> [!NOTE]
> Converting a `Decimal` to a `number` with `.toNumber()` may lose precision. Use `.toString()` when precision matters.

## Uuid

A [`Uuid`](/docs/reference/javascript/api/values/uuid.md) represents a universally unique identifier. The class supports generating both v4 (random) and v7 (time-ordered) UUIDs.

```ts
import { Uuid } from 'surrealdb';

const random = Uuid.v4();
const timeOrdered = Uuid.v7();
const parsed = Uuid.parse('550e8400-e29b-41d4-a716-446655440000');
```

## Range

A [`Range`](/docs/reference/javascript/api/values/range.md) is a bounded or open-ended span of values. In SurrealQL you use ranges to slice record IDs or filter numbers and times. `RecordIdRange` is the variant for a table ID range.

```ts
import { Range, RecordIdRange } from 'surrealdb';

const numericRange = new Range(1, 10);

const idRange = new RecordIdRange('users', { begin: 'a', end: 'f' });
const slice = await db.select(idRange);
```

## FileRef

A `FileRef` points at a file stored in SurrealDB. You see one when working with [file uploads](/docs/reference/query-language/language-primitives/data-types/files.md); it carries bucket and key metadata.

```ts
const [record] = await db.query('SELECT avatar FROM user:john');

if (record.avatar instanceof FileRef) {
    console.log(record.avatar.bucket);
    console.log(record.avatar.key);
}
```

## Geometry types

The SDK provides classes for all [GeoJSON geometry types](/docs/reference/query-language/language-primitives/data-types/geometries.md): `GeometryPoint`, `GeometryLine`, `GeometryPolygon`, `GeometryMultiPoint`, `GeometryMultiLine`, `GeometryMultiPolygon`, and `GeometryCollection`.

```ts
import { GeometryPoint, GeometryLine, GeometryPolygon } from 'surrealdb';

const point = new GeometryPoint([longitude, latitude]);
const line = new GeometryLine([
    new GeometryPoint([1, 2]),
    new GeometryPoint([3, 4]),
]);
```

## Parsing from strings

Most classes accept the same string forms as SurrealQL. Invalid input throws.

```ts
const recordId = RecordId.parse('users:john');
const datetime = DateTime.parse('2024-01-15T12:00:00Z');
const duration = Duration.parse('1h30m45s');
const uuid = Uuid.parse('550e8400-e29b-41d4-a716-446655440000');
```

## Using native dates

By default, datetimes come back as `DateTime`. Set `useNativeDates` in [codec options](/docs/reference/javascript/concepts/codecs.md#codec-options) if you would rather work with `Date` and can accept millisecond precision.

```ts
const db = new Surreal({
    codecOptions: {
        useNativeDates: true,
    },
});
```

## String prefixes

The `surrealdb` package includes tagged templates that mirror SurrealQL's `s`, `d`, `r`, and `u` prefixes. They are not part of `@surrealdb/sqon`.

```ts
import { s, d, r, u } from 'surrealdb';

const string = s`I am a string`;
const date = d`2024-05-06T17:44:57.085Z`;
const record = r`person:tobie`;
const uuid = u`92b84bde-39c8-4b4b-92f7-626096d6c4d9`;
```

## Best practices

### Use type parameters for type-safe queries

```ts
const users = new Table<User>('users');
const results: User[] = await db.select(users);

const userId = new RecordId<'users', string>('users', 'john');
```

### Prefer value classes over raw strings

```ts
await db.select(new RecordId('users', 'john'));

// Avoid string-based queries when possible
await db.query('SELECT * FROM users:john');
```

### Validate parsed input

```ts
try {
    const uuid = Uuid.parse(userInput);
} catch (error) {
    console.error('Invalid UUID format');
}
```

## Learn more

- [Data types API reference](/docs/reference/javascript/api/values/) for the full list of value class documentation
- [Codecs](/docs/reference/javascript/concepts/codecs.md) for serialising and deserialising value types
- [SurrealQL data model](/docs/reference/query-language/language-primitives/data-types.md) for the database-level type system
- [Utilities](/docs/reference/javascript/concepts/utilities.md) for comparing and converting values
