SurrealDB allows you to define custom API endpoints that expose database operations through HTTP-style routes. The JavaScript SDK provides the .api() method to invoke these endpoints with full type safety, custom headers, and structured responses.
| Method | Description |
|---|---|
db.api(prefix?) | Creates a SurrealApi instance for invoking user-defined endpoints |
api.get(path) | Invokes a GET endpoint |
api.post(path, body?) | Invokes a POST endpoint |
api.invoke(path, request?) | Invokes an endpoint with a custom request object |
To invoke user-defined endpoints, call .api() on any Surreal, SurrealSession, or SurrealTransaction instance. The returned SurrealApi object exposes HTTP-style methods like .get(), .post(), .put(), .delete(), and .patch().
// Obtain the API reference const api = db.api(); // Execute a GET request const users = await api.get('/users').value(); // Execute a POST request const newUser = await api.post('/users', { name: 'John Doe', email: 'john@example.com', }).value();
By default, API calls return a response object containing body, status, and headers. Chaining .value() returns only the response body directly.
const response = await api.get('/users'); console.log(response.status); // 200 console.log(response.headers); // { 'content-type': 'application/json' } console.log(response.body); // [ { id: RecordId, name: 'John Doe', email: 'john@example.com' } ] const users = await api.get('/users').value(); console.log(users); // [ { id: RecordId, name: 'John Doe', email: 'john@example.com' } ]
You can define TypeScript types for your API paths to get compile-time type checking on both request bodies and responses. Each path maps HTTP methods to a tuple of [RequestBody, ResponseBody].
// Define API types using an object literal type MyApi = { '/users': { get: [void, User[]]; post: [CreateUserInput, User]; }; [K: `/users/${string}`]: { get: [void, User]; put: [UpdateUserInput, User]; delete: [void, void]; }; }; // Pass the custom API types to the .api() method const api = db.api<MyApi>(); // All handlers will now be type-safe const users: User[] = await api.get('/users').value();
You can set headers on individual requests by chaining .header(), or set default headers on the API instance using api.header(). Setting a header value to null removes it.
// Single-request header const result = await api.get('/protected') .header('X-Custom-Header', 'value') .value(); // Global header api.header('Content-Type', 'application/json'); // Remove global header api.header('Content-Type', null);
When working with a group of related endpoints, you can pass a prefix to .api(). All subsequent calls will be relative to that prefix.
const usersApi = db.api<UserPaths>('/users'); const allUsers = await usersApi.get('/').value(); const user = await usersApi.get('/123').value();
When an API call fails, the SDK throws an UnsuccessfulApiError. This error includes the path, HTTP method, and the full response object.
import { UnsuccessfulApiError } from 'surrealdb'; try { await api.get('/users/999').value(); } catch (error) { if (error instanceof UnsuccessfulApiError) { console.error(`${error.method} ${error.path} failed`); console.error('Status:', error.response.status); } }