SurrealApi

The SurrealApi class exposes methods to interact with user-defined API endpoints in SurrealDB. It provides type-safe HTTP-style methods (GET, POST, PUT, DELETE, PATCH, TRACE) for invoking custom database APIs.

Source: api/api.ts

SurrealApi allows you to access custom API endpoints defined in your SurrealDB database. The class supports type-safe API definitions for better development experience.

// Define your API paths with types
type MyPaths = {
"/users": { get: [void, User[]] };
"/users/:id": { get: [void, User] };
"/users": { post: [CreateUserInput, User] };
};

// Access with type safety
const api = db.api<MyPaths>();
const users = await api.get("/users"); // Type: User[]

API instances are created through the api property on Surreal, SurrealSession, or SurrealTransaction:

// Basic API access
const api = db.api();

// Type-safe API access
const api = db.api<MyPaths>();

// API with path prefix
const usersApi = db.api<MyPaths>("/users");

Defines the HTTP methods available for an API path:

type PathDef = Partial<Record<HttpMethod, MethodDef>>;
type HttpMethod = "get" | "post" | "put" | "delete" | "patch" | "trace";
type MethodDef = [RequestBody, ResponseBody] | [];
type MyApiPaths = {
// GET endpoint with no request body, returns User[]
"/users": {
get: [void, User[]];
post: [CreateUserRequest, User];
};

// Dynamic path parameters
[K: `/users/${string}`]: {
get: [void, User];
put: [UpdateUserRequest, User];
delete: [void, void];
};

// POST endpoint with request/response bodies
"/auth/login": {
post: [{ email: string; password: string }, { token: string }];
};
};

Configure a header for all requests sent by this API instance. Useful for setting common headers like authentication tokens or content types.

Method Syntax

api.header(name, value)
ParameterTypeDescription
name stringThe name of the header to configure.
value string | nullThe value to set, or null to remove the header.

void

Set Custom Header

api.header('X-API-Key', 'my-secret-key');

Remove Header

api.header('X-API-Key', null);

Set Authorization Header

api.header('Authorization', `Bearer ${token}`);

Invoke a user-defined API with a custom request object. This is the generic method used by all HTTP method-specific functions.

Method Syntax

api.invoke<Req, Res>(path, request?)
ParameterTypeDescription
path stringThe API path to invoke.
request ApiRequest<Req>The request configuration object.

ApiPromise<Req, Res> - A promise for the API response

const result = await api.invoke('/custom', {
method: 'post',
body: { data: 'value' },
headers: { 'X-Custom': 'header' },
query: { filter: 'active' }
});

Invoke a user-defined GET API endpoint.

Method Syntax

api.get(path)
ParameterTypeDescription
path P extends ValidPaths<TPaths, "get">The API path to invoke.

ApiPromise<void, ResponseBody> - A promise for the GET response

Get All Users

const users = await api.get("/users");

Get Specific User

const user = await api.get("/users/123");

.post()

Invoke a user-defined POST API endpoint.

Method Syntax

api.post(path, body?)
ParameterTypeDescription
path P extends ValidPaths<TPaths, "post">The API path to invoke.
body RequestBody<TPaths, P, "post">The request body to send.

ApiPromise<RequestBody, ResponseBody> - A promise for the POST response

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

Invoke a user-defined PUT API endpoint.

Method Syntax

api.put(path, body?)
ParameterTypeDescription
path P extends ValidPaths<TPaths, "put">The API path to invoke.
body RequestBody<TPaths, P, "put">The request body to send.

ApiPromise<RequestBody, ResponseBody> - A promise for the PUT response

const updated = await api.put("/users/123", {
name: "John Smith",
email: "john.smith@example.com"
});

Invoke a user-defined DELETE API endpoint.

Method Syntax

api.delete(path, body?)
ParameterTypeDescription
path P extends ValidPaths<TPaths, "delete">The API path to invoke.
body RequestBody<TPaths, P, "delete">Optional request body.

ApiPromise<RequestBody, ResponseBody> - A promise for the DELETE response

await api.delete("/users/123");

Invoke a user-defined PATCH API endpoint.

Method Syntax

api.patch(path, body?)
ParameterTypeDescription
path P extends ValidPaths<TPaths, "patch">The API path to invoke.
body RequestBody<TPaths, P, "patch">The partial updates to apply.

ApiPromise<RequestBody, ResponseBody> - A promise for the PATCH response

const updated = await api.patch("/users/123", {
email: "newemail@example.com"
});

Invoke a user-defined TRACE API endpoint.

Method Syntax

api.trace(path, body?)
ParameterTypeDescription
path P extends ValidPaths<TPaths, "trace">The API path to invoke.
body RequestBody<TPaths, P, "trace">Optional request body.

ApiPromise<RequestBody, ResponseBody> - A promise for the TRACE response

import { Surreal } from 'surrealdb';

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

// Get API instance
const api = db.api();

// Make API calls
const users = await api.get('/users');
const user = await api.get('/users/123');
const created = await api.post('/users', {
name: 'New User',
email: 'user@example.com'
});
// Define your API contract
type ApiPaths = {
"/users": {
get: [void, User[]];
post: [CreateUserRequest, User];
};
[K: `/users/${string}`]: {
get: [void, User];
put: [UpdateUserRequest, User];
delete: [void, void];
};
"/auth/login": {
post: [LoginRequest, LoginResponse];
};
};

// Create type-safe API instance
const api = db.api<ApiPaths>();

// All calls are type-checked
const users: User[] = await api.get("/users");
const user: User = await api.get("/users/123");
const newUser: User = await api.post("/users", {
name: "Alice",
email: "alice@example.com"
});
const api = db.api();

// Set authentication header
const token = await login();
api.header('Authorization', `Bearer ${token}`);

// All subsequent requests include the header
const protected Data = await api.get('/protected-endpoint');

// Remove header
api.header('Authorization', null);
type UserPaths = {
"/": { get: [void, User[]] };
[K: `/${string}`]: {
get: [void, User];
put: [UpdateUserRequest, User];
delete: [void, void];
};
};

// Create API with prefix
const usersApi = db.api<UserPaths>("/users");

// Calls are prefixed automatically
const all Users = await usersApi.get("/"); // GET /users/
const user = await usersApi.get("/123"); // GET /users/123
const updated = await usersApi.put("/123", data); // PUT /users/123
const api = db.api();

try {
const user = await api.get('/users/999');
} catch (error) {
if (error instanceof ResponseError) {
console.error('API error:', error.message);
console.error('Status code:', error.code);
} else {
console.error('Unexpected error:', error);
}
}
const txn = await db.beginTransaction();

try {
// API calls within transaction
const api = txn.api();
const user = await api.post('/users', userData);
const profile = await api.post('/profiles', {
userId: user.id,
...profileData
});

await txn.commit();
} catch (error) {
await txn.cancel();
throw error;
}

Always define types for your API paths for better developer experience:

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

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

Create and reuse API instances rather than creating new ones for each call:

// Good: Reuse instance
const api = db.api();
await api.get('/users');
await api.get('/posts');

// Avoid: Creating multiple instances
await db.api().get('/users');
await db.api().get('/posts');

Use path prefixes to organize related endpoints:

const usersApi = db.api("/users");
const postsApi = db.api("/posts");

await usersApi.get("/123"); // GET /users/123
await postsApi.get("/456"); // GET /posts/456

Was this page helpful?