ApiPromise<Req, Res, V, J>

The ApiPromise class provides an interface for executing user-defined API endpoint calls. It extends Promise, allowing you to await it directly or configure the response handling.

Returned by: Methods on SurrealApi

Source: query/api.ts

Type Parameters

  • Req - The request body type

  • Res - The response body type

  • V - Boolean for value-only response (default: false)

  • J - Boolean indicating if result is JSON (default: false)

Configuration Methods

.json()

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

Method Syntax

apiPromise.json()

Returns

ApiPromise<Req, Res, V, true> - Promise returning JSON string

Example

const jsonString = await db.api().get('/users').json();
console.log(typeof jsonString); // 'string'

Append a header to the API request.

Method Syntax

apiPromise.header(name, value)

Parameters

ParameterTypeDescription
name stringThe header name.
value stringThe header value.

Returns

ApiPromise<Req, Res, V, J> - Chainable promise

Example

const result = await db.api().get('/users')
.header('X-Custom-Header', 'value')
.header('Authorization', 'Bearer token');

.value()

Return only the response body value, without the wrapper object.

Method Syntax

apiPromise.value()

Returns

ApiPromise<Req, Res, true, J> - Promise returning only the value

Examples

Without .value()

const response = await db.api().get('/users');
console.log(response.body); // The actual data
console.log(response.status); // HTTP status
console.log(response.headers); // Response headers

With .value()

const users = await db.api().get('/users').value();
console.log(users); // Direct access to user array

Response Structure

Default Response (without .value())

interface ApiResponse<T> {
body?: T;
headers?: Record<string, string>;
status?: number;
}

Value Response (with .value())

// Returns Res directly instead of ApiResponse<Res>

Complete Examples

Basic API Calls

import { Surreal } from 'surrealdb';

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

const api = db.api();

// GET request
const users = await api.get('/users').value();

// POST request
const newUser = await api.post('/users', {
name: 'John Doe',
email: 'john@example.com'
}).value();

// PUT request
const updated = await api.put('/users/123', {
name: 'John Smith'
}).value();

// DELETE request
await api.delete('/users/123').value();

With Full Response

const response = await db.api().get('/users');

console.log('Status:', response.status);
console.log('Headers:', response.headers);
console.log('Body:', response.body);

if (response.status === 200) {
console.log('Success:', response.body);
}

Custom Headers

const result = await db.api().post('/protected', data)
.header('Authorization', `Bearer ${token}`)
.header('X-API-Version', '2.0')
.value();

Type-Safe API Calls

interface User {
id: string;
name: string;
email: string;
}

interface CreateUserRequest {
name: string;
email: string;
password: string;
}

type ApiPaths = {
"/users": {
get: [void, User[]];
post: [CreateUserRequest, User];
};
};

const api = db.api<ApiPaths>();

// Type-safe calls
const users: User[] = await api.get('/users').value();
const newUser: User = await api.post('/users', {
name: 'Alice',
email: 'alice@example.com',
password: 'secure'
}).value();

Error Handling

try {
const user = await db.api().get('/users/999').value();
} catch (error) {
if (error instanceof UnsuccessfulApiError) {
console.error('API error:', error.response);
console.error('Status:', error.response.status);
console.error('Message:', error.response.body);
}
}

Pagination

async function fetchPage(page: number, pageSize: number) {
return db.api().get(`/users?page=${page}&limit=${pageSize}`).value();
}

const page1 = await fetchPage(1, 20);
const page2 = await fetchPage(2, 20);

File Upload

const formData = new FormData();
formData.append('file', fileBlob);
formData.append('name', 'profile-picture');

const result = await db.api().post('/upload', formData)
.header('Content-Type', 'multipart/form-data')
.value();

Authentication Flow

// Login
const loginResult = await db.api().post('/auth/login', {
email: 'user@example.com',
password: 'password123'
}).value();

const { access_token } = loginResult;

// Use token for subsequent requests
const api = db.api();
api.header('Authorization', `Bearer ${access_token}`);

const profile = await api.get('/profile').value();
const orders = await api.get('/orders').value();

Conditional Requests

const api = db.api();

// Check if resource exists
const checkResponse = await api.get('/users/123');

if (checkResponse.status === 404) {
// Create if doesn't exist
await api.post('/users/123', userData).value();
} else {
// Update if exists
await api.put('/users/123', userData).value();
}

Batch Operations

const api = db.api();

const promises = userIds.map(id =>
api.get(`/users/${id}`).value()
);

const users = await Promise.all(promises);
console.log(`Fetched ${users.length} users`);

Response Transformation

const response = await db.api().get('/users');

const transformed = {
data: response.body,
timestamp: new Date(),
status: response.status,
cached: response.headers?.['X-Cache'] === 'HIT'
};

Retry Pattern

async function apiWithRetry(
apiCall: () => Promise<any>,
maxRetries = 3
) {
for (let i = 0; i < maxRetries; i++) {
try {
return await apiCall();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
}
}

const result = await apiWithRetry(() =>
db.api().get('/unstable-endpoint').value()
);

Query Parameters

// Build query params manually
const params = new URLSearchParams({
filter: 'active',
sort: 'created_at',
order: 'desc'
});

const users = await db.api().get(`/users?${params}`).value();

WebSocket vs API Endpoints

// Both use the same connection
const db = new Surreal();
await db.connect('ws://localhost:8000');

// Regular query (via WebSocket/HTTP RPC)
const users1 = await db.select(new Table('users'));

// API endpoint (via defined API routes)
const users2 = await db.api().get('/users').value();

// Both work, but API endpoints allow custom logic

Response Headers

const response = await db.api().get('/users');

console.log('Content-Type:', response.headers?.['Content-Type']);
console.log('Cache-Control:', response.headers?.['Cache-Control']);
console.log('X-Custom:', response.headers?.['X-Custom-Header']);

Best Practices

1. Use Type Definitions

// Good: Type-safe API
type MyApi = {
"/users": { get: [void, User[]] };
};
const api = db.api<MyApi>();

// Avoid: Untyped
const api = db.api();

2. Use .value() for Simpler Code

// Good: Direct value access
const users = await api.get('/users').value();

// More verbose:
const response = await api.get('/users');
const users = response.body;

3. Handle Errors Gracefully

// Good: Specific error handling
try {
const result = await api.get('/users').value();
} catch (error) {
if (error instanceof UnsuccessfulApiError) {
// Handle API errors
}
}

See Also