# UpsertPromise

UpsertPromise provides chainable methods for configuring UPSERT operations (insert or replace).

The `UpsertPromise` class provides a chainable interface for configuring UPSERT operations (insert if not exists, replace if exists). It extends `Promise`, allowing you to `await` it directly or chain configuration methods.

> [!WARNING]
> UPSERT replaces the entire record if it exists. Use [`update().merge()`](/docs/reference/javascript/api/queries/update-promise.md#merge) for partial updates.

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

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

## Type parameters

- `T` - The result type
- `I` - The input type for record data
- `J` - Boolean indicating if result is JSON (default: `false`)

## Configuration methods

### `.content()` {#content}

Set the complete content for the record (insert or replace).

```ts title="Method Syntax"
upsertPromise.content(data)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>data</code> <label label="required" /></td>
            <td><code>Values&lt;I&gt;</code></td>
            <td>Complete record data (excluding id field).</td>
        </tr>
    </tbody>
</table>

#### Returns
`UpsertPromise<T, I, J>` - Chainable promise

#### Example

```ts
const user = await db.upsert(new RecordId('users', 'john'))
    .content({
        name: 'John Doe',
        email: 'john@example.com',
        age: 30
    });
// Inserts if not exists, replaces entirely if exists
```

---

### `.merge()` {#merge}

Merge data into the record (insert if not exists, merge if exists).

```ts title="Method Syntax"
upsertPromise.merge(data)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>data</code> <label label="required" /></td>
            <td><code>Values&lt;I&gt;</code></td>
            <td>Partial data to merge.</td>
        </tr>
    </tbody>
</table>

#### Returns
`UpsertPromise<T, I, J>` - Chainable promise

#### Example

```ts
const user = await db.upsert(new RecordId('users', 'john'))
    .merge({
        name: 'John Doe',
        last_login: DateTime.now()
    });
// If exists: merges fields; if not: creates with these fields
```

---

### `.replace()` {#replace}

Replace specific fields.

```ts title="Method Syntax"
upsertPromise.replace(data)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>data</code> <label label="required" /></td>
            <td><code>Values&lt;I&gt;</code></td>
            <td>Fields to replace.</td>
        </tr>
    </tbody>
</table>

#### Returns
`UpsertPromise<T, I, J>` - Chainable promise

---

### `.patch()` {#patch}

Apply JSON Patch operations.

```ts title="Method Syntax"
upsertPromise.patch(operations)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>operations</code> <label label="required" /></td>
            <td><code>Values&lt;I&gt;</code></td>
            <td>JSON Patch operations.</td>
        </tr>
    </tbody>
</table>

#### Returns
`UpsertPromise<T, I, J>` - Chainable promise

---

### `.where()` {#where}

Add a WHERE clause for conditional upsert.

```ts title="Method Syntax"
upsertPromise.where(expr)
```

#### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>expr</code> <label label="required" /></td>
            <td><code>ExprLike</code></td>
            <td>The condition expression (string or <a href="/docs/reference/javascript/api/utilities/expr.md">Expression</a> object).</td>
        </tr>
    </tbody>
</table>

#### Returns
`UpsertPromise<T, I, J>` - Chainable promise

---

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

Specify what to return.

```ts title="Method Syntax"
upsertPromise.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>"BEFORE"</code>, <code>"AFTER"</code>, <code>"DIFF"</code>, or field list.</td>
        </tr>
    </tbody>
</table>

#### Returns
`UpsertPromise<T, I, J>` - Chainable promise

---

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

Set operation timeout.

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

#### Returns
`UpsertPromise<T, I, 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"
upsertPromise.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
`UpsertPromise<T, I, J>` - Chainable promise

#### Example

```ts
await db.upsert(new RecordId('users', 'john'))
    .merge({ visits: 1 })
    .retry();
```

---

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

Return result as JSON string.

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

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

---

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

Compile the query into a `BoundQuery` without executing it.

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

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

#### Example

```ts
const query = db.upsert(new RecordId('users', 'john'))
    .content(userData)
    .compile();
```

---

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

Stream results as they arrive.

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

#### Returns
`AsyncIterableIterator` - Async iterator

## Complete examples

### Basic upsert

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

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

// Upsert: insert if not exists, replace if exists
const user = await db.upsert(new RecordId('users', 'john'))
    .content({
        name: 'John Doe',
        email: 'john@example.com',
        role: 'user'
    });
```

### Upsert with merge

```ts
// Safer: merge instead of replace
const user = await db.upsert(new RecordId('users', 'john'))
    .merge({
        last_login: DateTime.now(),
        login_count: 1
    });
// If user exists: only updates these fields
// If not: creates user with these fields
```

### Bulk upsert

```ts
const users = await db.upsert(new Table('users'))
    .content(userDataArray);
```

### Track changes

```ts
const result = await db.upsert(new RecordId('users', 'john'))
    .content(userData)
    .output('DIFF');

if (result) {
    console.log('Created or updated:', result);
}
```

### Conditional upsert

```ts
const user = await db.upsert(new RecordId('users', 'john'))
    .merge({ status: 'active' })
    .where('verified = true');
```

## UPSERT vs CREATE vs UPDATE

```ts
// CREATE: Fails if record exists
try {
    await db.create(recordId).content(data);
} catch (error) {
    // Error if exists
}

// UPDATE: Fails if record doesn't exist
try {
    await db.update(recordId).merge(data);
} catch (error) {
    // Error if not found
}

// UPSERT: Works in both cases
await db.upsert(recordId).content(data);
// Always succeeds
```

## Use cases

### Session management

```ts
// Update session or create new one
async function updateSession(sessionId: string, data: SessionData) {
    return db.upsert(new RecordId('sessions', sessionId))
        .merge({
            ...data,
            last_activity: DateTime.now()
        });
}
```

### Cache pattern

```ts
// Write-through cache
async function cacheSet(key: string, value: unknown) {
    return db.upsert(new RecordId('cache', key))
        .content({
            value,
            expires_at: DateTime.now().plus(Duration.parse('1h'))
        });
}
```

### Counter pattern

```ts
// Increment counter or initialize
const counter = await db.upsert(new RecordId('counters', 'page_views'))
    .merge({
        count: 1,
        last_increment: DateTime.now()
    });
```

## Chaining pattern

```ts
const result = await db.upsert(new RecordId('users', 'john'))
    .content(userData)
    .output('AFTER')
    .timeout(Duration.parse('5s'));
```

## See also

- [SurrealQueryable.upsert()](/docs/reference/javascript/api/core/surreal-queryable.md#upsert) - Method that returns UpsertPromise
- [CreatePromise](/docs/reference/javascript/api/queries/create-promise.md) - Create only
- [UpdatePromise](/docs/reference/javascript/api/queries/update-promise.md) - Update only
- [Query overview](/docs/reference/javascript/api/queries/) - All query builder classes
