# RunPromise

RunPromise provides methods for executing SurrealDB functions and SurrealML models.

The `RunPromise` class provides an interface for executing SurrealDB functions and SurrealML models. It extends `Promise`, allowing you to `await` it directly or use configuration methods.

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

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

## Type parameters

- `T` - The return type of the function
- `J` - Boolean indicating if result is JSON (default: `false`)

## Configuration methods

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

Configure the query to return the result as a JSON string.

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

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

#### Example

```ts
const jsonResult = await db.run('fn::calculate', [10, 20]).json();
console.log(typeof jsonResult); // 'string'
```

---

### `.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"
runPromise.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
`RunPromise<T, J>` - Chainable promise

#### Example

```ts
await db.run('fn::allocate_inventory', [productId, 10]).retry({ attempts: 3 });
```

---

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

Compile the query into a BoundQuery.

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

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

---

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

Stream the function result.

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

#### Returns
`AsyncIterableIterator<Frame<T, J>>` - Async iterator

## Complete examples

### Built-in functions

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

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

// Time functions
const now = await db.run('time::now');
console.log('Current time:', now);

const timestamp = await db.run('time::unix');
console.log('Unix timestamp:', timestamp);

// Array functions
const unique = await db.run('array::distinct', [[1, 2, 2, 3, 3, 3]]);
console.log('Unique values:', unique); // [1, 2, 3]

// String functions
const upper = await db.run('string::uppercase', ['hello world']);
console.log(upper); // 'HELLO WORLD'

// Math functions
const rounded = await db.run('math::round', [3.14159]);
console.log(rounded); // 3

// Crypto functions
const hash = await db.run('crypto::md5', ['password123']);
console.log('Hash:', hash);
```

### Custom functions

```ts
// First, define a custom function in SurrealDB
await db.query(`
    DEFINE FUNCTION fn::calculate_total($items: array) -> number {
        RETURN math::sum($items.map(|$item| $item.price * $item.quantity));
    };
`).collect();

// Then call it
const items = [
    { price: 10, quantity: 2 },
    { price: 5, quantity: 3 }
];

const total = await db.run('fn::calculate_total', [items]);
console.log('Total:', total); // 35
```

### SurrealML models

```ts
// Run a machine learning model
const prediction = await db.run(
    'ml::predict_sentiment',
    '1.0.0', // Model version
    ['This is a great product!']
);

console.log('Sentiment:', prediction);
```

### With type safety

```ts
interface CalculationResult {
    sum: number;
    average: number;
    count: number;
}

const result = await db.run<CalculationResult>(
    'fn::calculate_stats',
    [[10, 20, 30, 40, 50]]
);

console.log('Sum:', result.sum);
console.log('Average:', result.average);
console.log('Count:', result.count);
```

### Complex function arguments

```ts
// Function with multiple arguments
const result = await db.run('fn::process_order', [
    new RecordId('users', 'john'),
    new RecordId('products', 'widget'),
    {
        quantity: 5,
        shipping_address: '123 Main St',
        payment_method: 'credit_card'
    }
]);
```

### Parameterised functions

```ts
// Function that uses session variables
await db.set('discount_rate', 0.1);

const total = await db.run('fn::calculate_price', [
    100, // base price
    // Function can access $discount_rate
]);
```

### Error handling

```ts
try {
    const result = await db.run('fn::risky_operation', [data]);
} catch (error) {
    if (error instanceof ResponseError) {
        console.error('Function failed:', error.message);
    }
}
```

### Batch function calls

```ts
const results = await db.query(`
    RETURN fn::process(${data1});
    RETURN fn::process(${data2});
    RETURN fn::process(${data3});
`).collect();

console.log('Batch results:', results);
```

### With transaction

```ts
const txn = await db.beginTransaction();

try {
    // Call function within transaction
    const result = await txn.run('fn::allocate_inventory', [
        new RecordId('products', 'widget'),
        10
    ]);
    
    // Other transaction operations
    await txn.create(new Table('orders')).content({
        product: result.product,
        allocated: result.quantity
    });
    
    await txn.commit();
} catch (error) {
    await txn.cancel();
}
```

### Scheduled functions

```ts
// Define a scheduled function
await db.query(`
    DEFINE FUNCTION fn::cleanup_old_records() {
        DELETE FROM logs WHERE created_at < time::now() - 30d;
    };
`).collect();

// Run manually
await db.run('fn::cleanup_old_records');
```

### Recursive functions

```ts
await db.query(`
    DEFINE FUNCTION fn::factorial($n: number) -> number {
        IF $n <= 1 {
            RETURN 1
        } ELSE {
            RETURN $n * fn::factorial($n - 1)
        }
    };
`).collect();

const result = await db.run('fn::factorial', [5]);
console.log('5! =', result); // 120
```

### Data transformation

```ts
// Define transformation function
await db.query(`
    DEFINE FUNCTION fn::format_user($user: record) -> object {
        RETURN {
            full_name: string::concat($user.first_name, ' ', $user.last_name),
            email: string::lowercase($user.email),
            age: time::year(time::now()) - time::year($user.birth_date)
        };
    };
`).collect();

// Use it
const user = await db.select(new RecordId('users', 'john'));
const formatted = await db.run('fn::format_user', [user]);
console.log(formatted);
```

### ML model versions

```ts
// Run specific model version
const v1Result = await db.run('ml::classify_image', '1.0.0', [imageData]);
const v2Result = await db.run('ml::classify_image', '2.0.0', [imageData]);

console.log('v1 prediction:', v1Result);
console.log('v2 prediction:', v2Result);
```

### Validation functions

```ts
await db.query(`
    DEFINE FUNCTION fn::validate_email($email: string) -> bool {
        RETURN string::is::email($email);
    };
`).collect();

const isValid = await db.run('fn::validate_email', ['user@example.com']);

if (isValid) {
    // Proceed with user creation
}
```

## Best practices

### 1. Type function results

```ts
// Good: Type the result
interface Stats {
    count: number;
    average: number;
}

const stats = await db.run<Stats>('fn::calculate_stats', [data]);
stats.count; // TypeScript knows this exists

// Avoid: Untyped
const stats = await db.run('fn::calculate_stats', [data]);
stats.count; // No type safety
```

### 2. Handle function errors

```ts
// Good: Handle errors
try {
    const result = await db.run('fn::risky_operation', [data]);
} catch (error) {
    console.error('Function failed:', error);
}

// Consider: Implement error handling in the function itself
await db.query(`
    DEFINE FUNCTION fn::safe_operation($data: any) {
        TRY {
            RETURN fn::risky_operation($data);
        } CATCH {
            RETURN { error: true, message: 'Operation failed' };
        }
    };
`).collect();
```

### 3. Validate function names

Function names must follow the pattern: `namespace::function_name`

```ts
// Good: Valid function names
await db.run('fn::my_function', []);
await db.run('custom::calculate', []);
await db.run('ml::predict', '1.0.0', []);

// Invalid: Will throw error
await db.run('invalid name', []); // No namespace
await db.run('fn:bad-name', []); // Invalid characters
```

## See also

- [SurrealQueryable.run()](/docs/reference/javascript/api/core/surreal-queryable.md#run) - Method that returns RunPromise
- [Functions](/docs/reference/query-language/statements/define/function.md) - SurrealQL function definitions
- [Query overview](/docs/reference/javascript/api/queries/) - All query builder classes
