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 typeRes- The response body typeV- 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.
apiPromise.json()Returns
`ApiPromise` - Promise returning JSON string
Example
const jsonString = await db.api().get('/users').json();
console.log(typeof jsonString); // 'string' .header()
Append a header to the API request.
apiPromise.header(name, value)Parameters
Parameter | Type | Description |
|---|---|---|
name | string | The header name. |
value | string | The header value. |
Returns
`ApiPromise` - 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.
apiPromise.value()Returns
`ApiPromise` - Promise returning only the value
Examples
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 headersconst users = await db.api().get('/users').value();
console.log(users); // Direct access to user array .compile()
Compile the query into a BoundQuery.
apiPromise.compile()Returns
`BoundQuery` - The compiled query
.stream()
Stream results as they are received.
apiPromise.stream()Returns
`AsyncIterable` - Async iterable of response frames
.query()
Append a URL query parameter to the API request.
apiPromise.query(name, value)Parameters
Parameter | Type | Description |
|---|---|---|
name | string | The query parameter name. |
value | string | The query parameter value. |
Returns
`ApiPromise` - A new ApiPromise with the query parameter appended
Example
const result = await db.api().get('/users')
.query('filter', 'active')
.query('sort', 'created_at')
.value();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 logicResponse 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
SurrealApi - API instance methods
SurrealQueryable.api - Creating API instances
User-Defined APIs - Defining APIs in SurrealDB
Query overview - All query builder classes