| name | toolkit |
| description | Toolkit client usage for AIClient, AppDBClient, WorkflowClient, and CodeEngineClient. |
@domoinc/toolkit - Service Clients
The toolkit provides typed client classes for Domo services.
yarn add @domoinc/toolkit
import {
AppDBClient,
AIClient,
IdentityClient,
SqlClient,
UserClient,
GroupClient,
FileClient,
CodeEngineClient,
WorkflowClient,
DomoClient
} from '@domoinc/toolkit';
Response Wrapper
Most toolkit methods return a response wrapper:
interface ToolkitResponse<T> {
body: T;
status: number;
headers?: Record<string, string>;
}
const response = await IdentityClient.get();
const user = response.body;
const status = response.status;
Exception: AIClient often returns payload under response.data. Handle AI responses with:
const body = response.data || response.body || response;
const output = body.output || body.choices?.[0]?.output;
AppDBClient - NoSQL Document Store
PREFERRED for CRUD apps and long text storage. Collections can sync to Domo datasets.
DocumentsClient
interface Task {
title: string;
description: string;
status: 'active' | 'completed';
priority: 'Low' | 'High' | 'Urgent';
dueDate?: string;
}
const tasksClient = new AppDBClient.DocumentsClient<Task>('TasksCollection');
Create Documents:
const response = await tasksClient.create({
title: 'New Task',
description: 'Task description',
status: 'active',
priority: 'High'
});
const newTask = response.body;
const response = await tasksClient.create([
{ title: 'Task 1', status: 'active', priority: 'Low' },
{ title: 'Task 2', status: 'active', priority: 'High' }
]);
Read/Query Documents:
const response = await tasksClient.get();
const tasks = response.body;
const activeTasks = await tasksClient.get({
status: { $eq: 'active' }
});
const highPriority = await tasksClient.get({
priority: { $in: ['High', 'Urgent'] },
status: { $ne: 'completed' }
});
const filtered = await tasksClient.get({
$and: [
{ status: { $eq: 'active' } },
{ priority: { $in: ['High', 'Urgent'] } }
]
});
const either = await tasksClient.get({
$or: [
{ priority: { $eq: 'Urgent' } },
{ status: { $eq: 'completed' } }
]
});
const paginated = await tasksClient.get(
{ status: { $eq: 'active' } },
{
limit: 10,
offset: 0,
orderby: ['priority', 'dueDate']
}
);
const statusCounts = await tasksClient.get(
{},
{
groupby: ['status'],
count: 'id'
}
);
const priorityStats = await tasksClient.get(
{},
{
groupby: ['priority'],
count: 'id',
avg: ['estimatedHours']
}
);
CRITICAL response shape note:
.get() returns document wrappers with metadata; your app fields are in doc.content.
- Do not assume
docs[0].fieldName; use docs[0].content.fieldName.
const response = await tasksClient.get();
const rawDocs = response.body || response;
const docs = Array.isArray(rawDocs) ? rawDocs : [];
const parsed = docs.map((doc) => ({
id: doc.id,
...doc.content
}));
MongoDB Query Operators:
$eq - Equals
$ne - Not equals
$gt - Greater than
$gte - Greater than or equal
$lt - Less than
$lte - Less than or equal
$in - In array
$nin - Not in array
$regex - Regular expression match
$and - Logical AND
$or - Logical OR
Update Documents:
const response = await tasksClient.update({
id: 'document-uuid',
content: {
title: 'Updated Title',
description: 'Updated description',
status: 'completed',
priority: 'Low'
}
});
const bulkResponse = await tasksClient.update([
{ id: 'uuid-1', content: { title: 'Updated 1', status: 'active', priority: 'Low' } },
{ id: 'uuid-2', content: { title: 'Updated 2', status: 'active', priority: 'High' } }
]);
const count = await tasksClient.partialUpdate(
{ status: { $eq: 'active' } },
{ $set: { status: 'archived' } }
);
await tasksClient.partialUpdate(
{ priority: { $eq: 'Low' } },
{
$set: { priority: 'Medium', updatedAt: new Date().toISOString() },
$unset: { tempField: 1 },
$inc: { viewCount: 1 }
}
);
MongoDB Update Operators:
$set - Set field values
$unset - Remove fields
$inc - Increment numeric fields
$push - Add to array fields
Delete Documents:
await tasksClient.delete('document-uuid');
const response = await tasksClient.delete(['uuid-1', 'uuid-2', 'uuid-3']);
CollectionsClient
const collectionsClient = new AppDBClient.CollectionsClient();
const collections = await collectionsClient.list();
await collectionsClient.create({
name: 'NewCollection',
schema: {
columns: [
{ name: 'field1', type: 'STRING' },
{ name: 'field2', type: 'LONG' }
]
}
});
await collectionsClient.delete('CollectionName');
const exportData = await collectionsClient.export();
IdentityClient - Current User (SECURE)
Use this instead of Domo.env for security-sensitive operations.
const response = await IdentityClient.get();
const user = response.body;
const response = await IdentityClient.get(
5000,
false
);
SqlClient - Direct SQL Queries
Note: SQL endpoint does NOT respect page filters. Use Query endpoint for dashboard-embedded apps.
const sqlClient = new SqlClient();
const response = await sqlClient.get(
'sales-dataset-alias',
'SELECT region, SUM(amount) as total FROM sales-dataset-alias GROUP BY region ORDER BY total DESC'
);
const result = response.body;
const response = await sqlClient.get(
'sales',
`SELECT
DATE_TRUNC('month', order_date) as month,
product_category,
SUM(revenue) as revenue,
COUNT(*) as orders
FROM sales
WHERE order_date >= '2024-01-01'
GROUP BY 1, 2
ORDER BY 1, 3 DESC`
);
const predicates = SqlClient.parsePageFilters(['dataset1', 'dataset2']);
const clauses = SqlClient.parsePageFilters(['dataset1'], true);
UserClient - User API
const response = await UserClient.get(
50,
0,
true
);
const users = response.body;
const response = await UserClient.getUser(
12345,
true
);
const user = response.body;
const avatarBlob = await UserClient.getAvatar(
'avatar-key-from-user',
'medium'
);
GroupClient - Groups API
const response = await GroupClient.get(50, 0);
const groups = response.body;
const response = await GroupClient.getGroup(456);
const group = response.body;
const response = await GroupClient.getMembers(456);
const members = response.body;
FileClient - File Storage
const fileClient = new FileClient();
const response = await fileClient.upload(
fileObject,
'report.pdf',
'Monthly report',
true,
10000
);
const fileId = response.body.id;
const response = await fileClient.uploadRevision(
newFileObject,
existingFileId
);
const blob = await fileClient.download(
fileId,
'filename.pdf',
revisionId
);
const response = await fileClient.detailsList(
[123, 456],
['metadata', 'permissions', 'revisions']
);
const perms = await fileClient.getPermissions(fileId);
await fileClient.updatePermissions(fileId, JSON.stringify({
public: false,
users: [123, 456],
groups: [789]
}));
await fileClient.delete(fileId, revisionId);
AIClient - AI Services
All methods are static and use snake_case naming.
Response Structure:
IMPORTANT: AIClient uses data instead of body for the response payload! The toolkit wraps responses in { data, status, statusCode }. The API response includes both a top-level output field and a choices array. Prefer the top-level output field when available.
const response = await AIClient.generate_text(
'Explain this sales trend in simple terms',
{ template: 'You are a business analyst. ${input}' },
{ tone: 'professional' },
'model-id',
{ temperature: 0.7 }
);
const responseBody = response.data || response.body || response;
const text = responseBody.output || responseBody.choices?.[0]?.output;
const response = await AIClient.text_to_sql(
'Show me total sales by region for Q4',
[{
dataSourceName: 'Sales',
description: 'Sales transactions table',
columns: [
{ name: 'region', type: 'string' },
{ name: 'amount', type: 'number' },
{ name: 'order_date', type: 'date' }
]
}]
);
const responseBody = response.data || response.body || response;
const sql = responseBody.output || responseBody.choices?.[0]?.output;
const response = await AIClient.text_to_beastmode(
'Calculate year over year growth percentage',
{
dataSourceName: 'Revenue',
columns: [
{ name: 'revenue', type: 'number' },
{ name: 'date', type: 'date' }
]
}
);
const responseBody = response.data || response.body || response;
const beastMode = responseBody.output || responseBody.choices?.[0]?.output;
const response = await AIClient.summarize(
longTextContent,
undefined,
undefined,
undefined,
undefined,
undefined,
[],
{ separatorType: 'paragraph' },
'bullets',
100
);
const responseBody = response.data || response.body || response;
const summary = responseBody.output || responseBody.choices?.[0]?.output;
CodeEngineClient - Serverless Functions
In app implementations, prefer the working direct-call pattern via domo.post and packagesMapping contracts.
If you use Toolkit docs examples, verify runtime behavior in your environment.
import domo from 'ryuu.js';
const response = await domo.post('/domo/codeengine/v2/packages/calculateTax', {
amount: 1000,
state: 'CA'
});
console.log('Code Engine response:', response);
const body = response?.body ?? response?.data ?? response;
const result =
body?.output ??
body?.result ??
body?.value ??
body;
Manifest note: use packagesMapping (with s) and include full parameter/output schema fields.
WorkflowClient - Domo Workflows
const response = await WorkflowClient.getAllModels();
const models = response.body;
const modelsWithPermissions = await WorkflowClient.getAllModels(true);
const response = await WorkflowClient.getModelDetails('myWorkflow');
const model = response.body;
const response = await WorkflowClient.startModel(
'myWorkflow',
{ inputVar: 'value', anotherVar: 123 }
);
const instance = response.body;
const response = await WorkflowClient.getInstance('myWorkflow', 'instance-uuid');
const status = response.body;
All WorkflowClient methods are alias-based in app code:
WorkflowClient.startModel(workflowAlias, variables)
WorkflowClient.getAllModels() / WorkflowClient.getAllModels(true)
WorkflowClient.getModelDetails(workflowAlias)
WorkflowClient.getInstance(workflowAlias, instanceId)
The workflow UUID lives in manifest.json under workflowMapping[].modelId; runtime client calls pass the alias.
DomoClient - Alternative to ryuu.js
const data = await DomoClient.get('/data/v1/sales');
await DomoClient.post('/api/items', { name: 'test' });
await DomoClient.put('/api/items/123', { name: 'updated' });
await DomoClient.delete('/api/items/123');