# RelatePromise

RelatePromise provides chainable methods for configuring RELATE operations for graph relationships.

The `RelatePromise` class provides a chainable interface for configuring RELATE operations to create graph relationships (edges) between records. It extends `Promise`, allowing you to `await` it directly or chain configuration methods.

**Returned by:** [`SurrealQueryable.relate()`](/docs/reference/javascript/api/core/surreal-queryable.md#relate)

**Source:** [query/relate.ts](https://github.com/surrealdb/surrealdb.js/blob/main/packages/sdk/src/query/relate.ts)

## Type parameters

- `T` - The result type (edge record type)
- `J` - Boolean indicating if result is JSON (default: `false`)

## Configuration methods

### `.unique()` {#unique}

Enforce a unique relationship constraint (only one edge between the same nodes).

```ts title="Method Syntax"
relatePromise.unique()
```

#### Returns
`RelatePromise<T, J>` - Chainable promise

#### Example

```ts
// Only allow one 'likes' edge between user and post
const edge = await db.relate(
    new RecordId('users', 'john'),
    new Table('likes'),
    new RecordId('posts', '1')
).unique();
// If edge already exists, this won't create a duplicate
```

---

### `.output()` {#output}

Specify what to return from the operation.

```ts title="Method Syntax"
relatePromise.output(fields)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>fields</code> <label label="required" /></td>
            <td><code>Output</code></td>
            <td><code>"NONE"</code>, <code>"AFTER"</code>, or specific field list.</td>
        </tr>
    </tbody>
</table>

#### Returns
`RelatePromise<T, J>` - Chainable promise

#### Example

```ts
const edge = await db.relate(
    new RecordId('users', 'john'),
    new Table('follows'),
    new RecordId('users', 'jane')
).output('id', 'in', 'out', 'created_at');
```

---

### `.timeout()` {#timeout}

Set a timeout for the operation.

```ts title="Method Syntax"
relatePromise.timeout(duration)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>duration</code> <label label="required" /></td>
            <td><code><a href="/docs/reference/javascript/api/values/duration.md">Duration</a></code></td>
            <td>Maximum time to wait.</td>
        </tr>
    </tbody>
</table>

#### Returns
`RelatePromise<T, J>` - Chainable promise

---

### `.version()` {#version}

Create the relationship at a specific version.

```ts title="Method Syntax"
relatePromise.version(timestamp)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>timestamp</code> <label label="required" /></td>
            <td><code><a href="/docs/reference/javascript/api/values/datetime.md">DateTime</a></code></td>
            <td>The version timestamp.</td>
        </tr>
    </tbody>
</table>

#### Returns
`RelatePromise<T, J>` - Chainable promise

---

### `.retry()` {#retry}

Retry the operation with exponential backoff if it fails due to a write conflict. Off by default; passing an options object (or calling with no arguments) opts the operation in.

This overrides the connection-wide default set via the [`retry`](/docs/reference/javascript/api/types/#connectoptions) option on `ConnectOptions`.

```ts title="Method Syntax"
relatePromise.retry(options?)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>options</code> <label label="optional" /></td>
            <td><code>Partial&lt;<a href="/docs/reference/javascript/api/types/#retryoptions">RetryOptions</a>&gt;</code></td>
            <td>Retry configuration. If omitted, retry is enabled with the default configuration.</td>
        </tr>
    </tbody>
</table>

#### Returns
`RelatePromise<T, J>` - Chainable promise

#### Example

```ts
await db.relate(alice, 'follows', bob).retry();
```

---

### `.json()` {#json}

Return result as JSON string.

```ts title="Method Syntax"
relatePromise.json()
```

#### Returns
`RelatePromise<T, true>` - Promise returning JSON string

---

### `.compile()` {#compile}

Compile the query into a BoundQuery.

```ts title="Method Syntax"
relatePromise.compile()
```

#### Returns
`BoundQuery<[T]>` - The compiled query

---

### `.stream()` {#stream}

Stream results as relationships are created.

```ts title="Method Syntax"
relatePromise.stream()
```

#### Returns
`AsyncIterableIterator` - Async iterator

## Complete examples

### Basic relationship

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

const db = new Surreal();
await db.connect('ws://localhost:8000');

// Create a single relationship
const edge = await db.relate(
    new RecordId('users', 'john'),
    new Table('likes'),
    new RecordId('posts', '1'),
    { created_at: DateTime.now() }
);

console.log('Edge created:', edge.id);
console.log('From:', edge.in);  // users:john
console.log('To:', edge.out);   // posts:1
```

### Multiple relationships

```ts
// Create multiple edges at once
const edges = await db.relate(
    [new RecordId('users', 'john'), new RecordId('users', 'jane')],
    new Table('follows'),
    [new RecordId('users', 'alice'), new RecordId('users', 'bob')]
);

// Creates 4 edges:
// john->follows->alice
// john->follows->bob
// jane->follows->alice
// jane->follows->bob
```

### Unique relationships

```ts
// Prevent duplicate 'likes'
const edge = await db.relate(
    new RecordId('users', 'john'),
    new Table('likes'),
    new RecordId('posts', '1'),
    { timestamp: DateTime.now() }
).unique();

// Second call won't create duplicate
const duplicate = await db.relate(
    new RecordId('users', 'john'),
    new Table('likes'),
    new RecordId('posts', '1')
).unique();
// Returns existing edge
```

### Relationship with data

```ts
const friendship = await db.relate(
    new RecordId('users', 'john'),
    new Table('friends'),
    new RecordId('users', 'jane'),
    {
        since: DateTime.parse('2024-01-15'),
        strength: 0.8,
        mutual: true,
        tags: ['colleague', 'neighbor']
    }
);
```

### Specific edge ID

```ts
// Use specific ID for the edge
const edge = await db.relate(
    new RecordId('users', 'john'),
    new RecordId('likes', 'specific-edge-id'),
    new RecordId('posts', '1'),
    { strength: 10 }
);
```

### Fan-out relationships

```ts
// One user follows many
const edges = await db.relate(
    new RecordId('users', 'john'),
    new Table('follows'),
    [
        new RecordId('users', 'alice'),
        new RecordId('users', 'bob'),
        new RecordId('users', 'carol')
    ]
);

console.log(`Created ${edges.length} follow edges`);
```

### Streaming bulk relationships

```ts
const users = await db.select(new Table('users'));
const popularPost = new RecordId('posts', 'viral-post');

const edges = db.relate(
    users.map(u => u.id),
    new Table('viewed'),
    popularPost,
    { viewed_at: DateTime.now() }
);

for await (const edge of edges.stream()) {
    console.log(`Created view edge: ${edge.id}`);
}
```

### Bidirectional relationships

```ts
// Create friendship in both directions
await db.relate(
    new RecordId('users', 'john'),
    new Table('friends'),
    new RecordId('users', 'jane')
);

await db.relate(
    new RecordId('users', 'jane'),
    new Table('friends'),
    new RecordId('users', 'john')
);
```

### Relationship metadata

```ts
const edge = await db.relate(
    new RecordId('users', 'john'),
    new Table('rated'),
    new RecordId('movies', 'inception'),
    {
        rating: 5,
        review: 'Amazing movie!',
        watched_at: DateTime.parse('2024-01-15'),
        platform: 'Netflix'
    }
);
```

### Temporal relationships

```ts
// Track when relationship was created
const edge = await db.relate(
    new RecordId('users', 'john'),
    new Table('employed_at'),
    new RecordId('companies', 'acme'),
    {
        started_at: DateTime.parse('2024-01-01'),
        position: 'Developer',
        department: 'Engineering'
    }
);
```

### Weighted graph

```ts
// Create weighted edges for graph algorithms
const edge = await db.relate(
    new RecordId('cities', 'new-york'),
    new Table('connected_to'),
    new RecordId('cities', 'boston'),
    {
        distance: 215,  // miles
        travel_time: Duration.parse('4h'),
        cost: 50
    }
);
```

### Delete and recreate pattern

```ts
// Remove existing relationship and create new one
const userId = new RecordId('users', 'john');
const postId = new RecordId('posts', '1');

// Delete existing edge
await db.query(
    surql`DELETE FROM likes WHERE in = ${userId} AND out = ${postId}`
).collect();

// Create new edge with updated data
const edge = await db.relate(
    userId,
    new Table('likes'),
    postId,
    { created_at: DateTime.now() }
);
```

## Graph traversal example

```ts
// Create relationships
await db.relate(
    new RecordId('users', 'john'),
    new Table('follows'),
    new RecordId('users', 'jane')
);

// Query traversal
const followers = await db.query(
    surql`SELECT <-follows<-users.* AS followers FROM users:jane`
).collect();

console.log('Jane has followers:', followers);
```

## Best practices

### 1. Use unique for one-to-one

```ts
// Good: Prevent duplicate likes
await db.relate(from, new Table('likes'), to).unique();

// Avoid: Allowing duplicates
await db.relate(from, new Table('likes'), to);
// May create multiple edges
```

### 2. Include metadata

```ts
// Good: Track when relationship was created
await db.relate(from, edge, to, {
    created_at: DateTime.now(),
    source: 'web-app'
});

// Basic: No metadata
await db.relate(from, edge, to);
```

### 3. Use specific edge IDs for updates

```ts
// Good: use a specific ID so you can update the edge later
const edgeId = new RecordId('likes', [from.toString(), to.toString()]);
await db.relate(from, edgeId, to, metadata);

// Later: update by record ID
await db.update(edgeId).merge({ updated_at: DateTime.now() });
```

As of SurrealDB 3.1.5, when the edge ID already exists, `INSERT RELATION` returns an error unless you use `ON DUPLICATE KEY UPDATE`. See [Explicit edge record IDs](/docs/reference/query-language/statements/relate.md#handling-duplicate-edge-record-ids) for more details.

## See also

- [SurrealQueryable.relate()](/docs/reference/javascript/api/core/surreal-queryable.md#relate) - Method that returns RelatePromise
- [Graph relationships](/docs/reference/query-language/statements/relate.md) - SurrealQL RELATE documentation
- [RecordId](/docs/reference/javascript/api/values.md#custom-data-type-classes) - Record identifier type
- [Query overview](/docs/reference/javascript/api/queries/) - All query builder classes
