Skip to content

DomoApps/domo.js

Repository files navigation

ryuu.js

A powerful JavaScript SDK for building custom applications within the Domo platform

npm version License TypeScript

ryuu.js (published as ryuu.js, developed as domo.js) is a comprehensive JavaScript library that enables developers to build interactive custom applications within the Domo platform. It provides seamless communication between your custom app and the Domo environment, supporting data fetching, real-time events, filters, variables, and cross-platform mobile integration.


📚 Documentation

Choose your version:


v6+ Documentation

Table of Contents


Quick Start

Get started with ryuu.js in just a few lines:

import Domo from 'ryuu.js';

// Fetch data from a dataset
const data = await Domo.get('/data/v1/sales');
console.log(data); // Array of objects with your data

// Listen for dataset updates
Domo.onDataUpdated((alias) => {
  console.log(`Dataset ${alias} was updated!`);
  // Refresh your visualization
});

// Listen for filter changes
Domo.onFiltersUpdated((filters) => {
  console.log('Filters changed:', filters);
  // Apply filters to your data
});

Features

  • Data API - Query datasets with typed options (fields, filters, aggregations, date graining, beast modes)
  • AppDB - Full CRUD, MongoDB queries, bulk operations, partial updates, collection management
  • Code Engine - Run Code Engine functions by manifest alias
  • Workflows - Start and monitor Domo Workflows
  • AI Services - Text generation and text-to-SQL via Domo's AI Service Layer
  • HTTP API Access - Authenticated requests to Domo datasets, datastores, and APIs
  • Real-time Events - Listen for dataset updates, filter changes, and variable updates
  • Filter Management - Get and set page-level filters programmatically
  • Variable Management - Access and update page variables
  • Custom App Data - Send custom data between apps on the same page
  • Typed Environment - Domo.env with userId, userName, host, platform, enriched from environment API
  • Navigation - Programmatically navigate within Domo
  • Mobile Support - Full iOS and Android compatibility
  • TypeScript Ready - Complete type definitions with typed callbacks and generics
  • Zero Dependencies - ~28KB UMD bundle with no runtime dependencies
  • Extensible - extend() propagates overrides to all services (data, appdb, ai, etc.)
  • Structured Errors - Typed error classes (DomoHttpError, DomoAuthError, DomoConnectionError, DomoValidationError) with instanceof support
  • Schema Validation - Optional runtime response validation via { schema: { parse } } option (works with zod, valibot, etc.)
  • Request Interceptors - Middleware pattern via Domo.intercept() for logging, retry, caching
  • Debug Mode - Domo.debug.enable() for category-based logging of HTTP, messages, filters, variables

Installation

NPM

npm install ryuu.js

Then import in your application:

// ES modules
import Domo from 'ryuu.js';

// CommonJS
const Domo = require('ryuu.js').default;

CDN / Script Tag

<script src="https://unpkg.com/ryuu.js"></script>
<script>
  // Domo is available globally
  Domo.get('/data/v1/sales').then(data => {
    console.log(data);
  });
</script>

TypeScript

TypeScript definitions are included automatically:

import Domo, { Filter, Variable, RequestOptions } from 'ryuu.js';

Core Concepts

Communication Architecture

Your custom app runs in an iframe within the Domo platform. ryuu.js establishes a bidirectional communication channel using:

  • MessageChannel API - Primary communication mechanism for desktop/web
  • webkit.messageHandlers - iOS native integration
  • Global objects - Android/Flutter integration

Request-Reply Pattern

Async operations (like updating filters) use an ASK-ACK-REPLY pattern:

  1. ASK - Your app sends a request with a unique ID
  2. ACK - Parent acknowledges receipt (optional callback)
  3. REPLY - Parent sends response when operation completes (optional callback)
Domo.requestFiltersUpdate(
  filters,
  true,
  () => console.log('Request acknowledged'),
  (response) => console.log('Request completed:', response)
);

Environment Context

Domo.env provides typed access to the current user, instance, and page context. Properties are populated immediately from iframe query parameters, then enriched in the background from GET /domo/environment/v1 (which provides authoritative server-side values like host).

// Available immediately
console.log(Domo.env.userId);      // "481303514"
console.log(Domo.env.userName);    // "JSON"
console.log(Domo.env.userEmail);   // "Jason.Hansen@domo.com"
console.log(Domo.env.customer);    // "domo"
console.log(Domo.env.locale);      // "en-US"
console.log(Domo.env.platform);    // "desktop" | "mobile"
console.log(Domo.env.pageId);      // "1185508957"

// Available after environment API loads
console.log(Domo.env.host);        // "domo.demo.domo.com"
console.log(Domo.env.loaded);      // true

The loaded property indicates whether the environment API has been fetched. If it fails (e.g. running locally), query params serve as the fallback.


API Reference

HTTP Methods

All HTTP methods return Promises and support multiple data formats.

Domo.get(url, options?)

Fetch data from a Domo dataset or API endpoint.

Parameters:

  • url (string) - API endpoint URL
  • options (object, optional) - Request options

Returns: Promise<ResponseBody>

Basic Usage:

// Returns array of objects by default
const data = await Domo.get('/data/v1/sales');
console.log(data); // [{ id: 1, amount: 100, ... }, ...]

Format Options:

// CSV format
const csv = await Domo.get('/data/v1/sales', { format: 'csv' });

// Array of arrays with metadata
const arrayData = await Domo.get('/data/v1/sales', { format: 'array-of-arrays' });
console.log(arrayData.columns);  // ['id', 'amount', 'date']
console.log(arrayData.rows);     // [[1, 100, '2024-01-01'], ...]

// Excel format (returns Blob)
const excel = await Domo.get('/data/v1/sales', { format: 'excel' });

Supported Formats:

  • 'array-of-objects' (default) - Returns ObjectResponseBody[]
  • 'array-of-arrays' - Returns ArrayResponseBody with metadata
  • 'csv' - Returns CSV string
  • 'excel' - Returns Excel Blob
  • 'plain' - Returns plain text string

Query Parameters:

const data = await Domo.get('/data/v1/sales', {
  query: {
    limit: 100,
    offset: 0,
    fields: 'id,amount,date'
  }
});

Best Practice: Always filter and paginate large datasets to avoid slow responses:

const data = await Domo.get('/data/v1/sales', {
  query: {
    limit: 1000,
    offset: 0,
    filter: 'date > "2024-01-01"'
  }
});

Domo.getAll(urls, options?)

Fetch multiple datasets or endpoints in parallel.

Parameters:

  • urls (string[]) - Array of API endpoint URLs
  • options (object, optional) - Request options applied to all requests

Returns: Promise<ResponseBody[]>

Example:

const [sales, inventory, customers] = await Domo.getAll([
  '/data/v1/sales',
  '/data/v1/inventory',
  '/data/v1/customers'
]);

console.log(sales);      // First dataset
console.log(inventory);  // Second dataset
console.log(customers);  // Third dataset

With Options:

const results = await Domo.getAll(
  ['/data/v1/sales', '/data/v1/inventory'],
  { format: 'csv' }
);
// All results will be CSV strings

Domo.post(url, body?, options?)

Send a POST request to create data.

Parameters:

  • url (string) - API endpoint URL
  • body (object | string, optional) - Request body
  • options (object, optional) - Request options

Returns: Promise<ResponseBody>

Example:

// Create a document in Domo DataStore
const result = await Domo.post(
  '/domo/datastores/v1/collections/users/documents/',
  {
    name: 'John Doe',
    email: 'john@example.com',
    role: 'Admin'
  }
);

console.log(result); // Created document with ID

Domo.put(url, body?, options?)

Send a PUT request to update data.

Parameters:

  • url (string) - API endpoint URL
  • body (object | string, optional) - Request body
  • options (object, optional) - Request options

Returns: Promise<ResponseBody>

Example:

// Update an existing document
const result = await Domo.put(
  '/domo/datastores/v1/collections/users/documents/abc123',
  {
    name: 'Jane Doe',
    role: 'Manager'
  }
);

Domo.delete(url, options?)

Send a DELETE request to remove data.

Parameters:

  • url (string) - API endpoint URL
  • options (object, optional) - Request options

Returns: Promise<ResponseBody>

Example:

// Delete a document
await Domo.delete('/domo/datastores/v1/collections/users/documents/abc123');

Domo.domoHttp(method, url, options?, body?)

Low-level HTTP method for full control over requests. All other HTTP methods use this internally.

Parameters:

  • method (RequestMethods) - HTTP method
  • url (string) - API endpoint URL
  • options (object, optional) - Request options
  • body (object | string, optional) - Request body

Returns: Promise<ResponseBody>

Example:

import { RequestMethods } from 'ryuu.js';

const result = await Domo.domoHttp(
  RequestMethods.PATCH,
  '/custom/endpoint',
  { format: 'array-of-objects' },
  { data: 'custom body' }
);

Data API

High-level helpers for querying datasets. Supports all Data API query operators.

Domo.data.query(alias, options?)

Query a dataset by its manifest alias.

// Simple query
const rows = await Domo.data.query("sales");

// With filtering, aggregation, and pagination
const totals = await Domo.data.query("sales", {
  fields: ["region", "amount"],
  filter: "amount > 100",
  sum: ["amount"],
  groupBy: ["region"],
  orderBy: "amount descending",
  limit: 50,
});

// Date graining
const monthly = await Domo.data.query("sales", {
  dateGrain: "orderDate by month",
  sum: ["amount"],
  calendar: "fiscal",
});

// Beast modes
const bm = await Domo.data.query("sales", {
  useBeastMode: true,
  fields: ["myBeastMode", "reps"],
  sum: ["myBeastMode"],
  groupBy: ["reps"],
});

// CSV format
const csv = await Domo.data.query("sales", { format: "csv" });

Supported options: fields, filter, avg, count, max, min, sum, unique, groupBy, dateGrain, calendar, orderBy, limit, offset, useBeastMode, format.

Domo.data.sql(alias, sqlQuery, options?)

Execute a SQL query against a dataset via POST /sql/v1/{alias}.

const rows = await Domo.data.sql("sales", "SELECT region, SUM(amount) as total FROM sales GROUP BY region");

Note: The SQL API does not support page filters or JOINs.


AppDB

Full CRUD, MongoDB-style queries, bulk operations, and collection management for AppDB.

Document CRUD

const docs = await Domo.appdb.list("Users");
const doc = await Domo.appdb.get("Users", docId);
const created = await Domo.appdb.create("Users", { username: "Bill" }); // auto-wraps in { content: ... }
await Domo.appdb.update("Users", docId, { username: "Ted" });
await Domo.appdb.remove("Users", docId);

Query with MongoDB Syntax

const docs = await Domo.appdb.query("Users", { "content.region": "West" });

// With aggregations
const results = await Domo.appdb.query("campaigns", {}, {
  groupby: "content.campaignName, content.month",
  count: "documentCount",
  sum: "content.clicks sumClicks",
  orderby: "sumClicks descending",
});

Partial Update (MongoDB Operators)

await Domo.appdb.partialUpdate("Users",
  { "content.username": "Bill" },
  { "$set": { "content.band": "Wyld Stallyns" } }
);

Bulk Operations

await Domo.appdb.bulkCreate("Users", [{ username: "Bill" }, { username: "Ted" }]);
await Domo.appdb.bulkUpsert("Users", [
  { id: "existing-id", username: "Bill", band: "Wyld Stallyns" },
  { username: "Rufus" },
]);
await Domo.appdb.bulkDelete("Users", ["id-1", "id-2", "id-3"]);

Collection Management & Export

await Domo.appdb.listCollections();
await Domo.appdb.createCollection({ name: "Users", schema: { columns: [{ name: "username", type: "STRING" }] }, syncEnabled: true });
await Domo.appdb.export();       // manually trigger sync to Domo DataSets

Code Engine

Run Code Engine functions by their manifest alias.

const result = await Domo.codeEngine("awesomeFunction", { number1AppInput: 5, number2AppInput: 10 });

Requires a packageMapping entry in your manifest.json.


Workflows

Start and monitor Domo Workflows.

const instance = await Domo.workflow.start("myWorkflow", { param1: "hello" });
const current = await Domo.workflow.getInstance("myWorkflow", instance.id);
// current.status: "IN_PROGRESS" | "COMPLETED" | "CANCELED" | "FAILED" | null

Requires a workflowMapping entry in your manifest.json.


AI Services

Access Domo's AI Service Layer for text generation and text-to-SQL.

const res = await Domo.ai.generateText("Tell me a joke about data");
console.log(res.choices[0].output);

const sql = await Domo.ai.textToSQL("Show total sales by region", {
  dataSourceSchemas: [{ dataSourceName: "Sales", columns: [{ name: "Region", type: "string" }, { name: "Amount", type: "number" }] }],
});
console.log(sql.choices[0].output); // "SELECT Region, SUM(Amount) ..."

Note: AI services consume AI credits. See your Domo instance rate card for details.


Event Listeners

Event listeners enable real-time reactivity to changes in the Domo platform. All listener methods return an unsubscribe function.

Domo.onDataUpdated(callback)

Listen for dataset update events.

Parameters:

  • callback (function) - Called when a dataset is updated
    • Receives: datasetAlias (string) - Alias of the updated dataset

Returns: function - Unsubscribe function

Example:

const unsubscribe = Domo.onDataUpdated((datasetAlias) => {
  console.log(`Dataset ${datasetAlias} was updated`);
  // Reload data for this dataset
  loadData();
});

// Later, stop listening
unsubscribe();

Use Case: Refresh your app's visualizations when underlying data changes without requiring a page reload.


Domo.onFiltersUpdated(callback)

Listen for page filter changes.

Parameters:

  • callback (function) - Called when filters change
    • Receives: filters (Filter[]) - Array of filter objects

Returns: function - Unsubscribe function

Example:

Domo.onFiltersUpdated((filters) => {
  console.log('Filters updated:', filters);

  // Find specific filter
  const categoryFilter = filters.find(f => f.column === 'category');

  if (categoryFilter) {
    console.log('Category filter:', categoryFilter.values);
    // Apply filter to your visualization
    applyFilters(categoryFilter.values);
  }
});

Filter Object Structure:

{
  column: "category",              // Column name being filtered
  operator: "IN",                  // Filter operator
  values: ["ALERT", "WARNING"],    // Array of filter values
  dataType: "STRING",              // Data type: STRING, NUMERIC, DATE, DATETIME
  dataSourceId: "46d91556-...",    // Source dataset ID (optional)
  label: "category"                // Display label (optional)
}

Supported Operators:

String Operators:

  • "IN" - Value is in list
  • "NOT_IN" - Value is not in list
  • "CONTAINS" - Value contains string
  • "NOT_CONTAINS" - Value doesn't contain string
  • "STARTS_WITH" - Value starts with string
  • "NOT_STARTS_WITH" - Value doesn't start with string
  • "ENDS_WITH" - Value ends with string
  • "NOT_ENDS_WITH" - Value doesn't end with string

Numeric/Date Operators:

  • "EQUALS" - Equals value
  • "NOT_EQUALS" - Not equals value
  • "GREATER_THAN" - Greater than value
  • "GREAT_THAN_EQUALS_TO" - Greater than or equals value
  • "LESS_THAN" - Less than value
  • "LESS_THAN_EQUALS_TO" - Less than or equals value
  • "BETWEEN" - Between two values

Domo.onVariablesUpdated(callback)

Listen for page variable changes.

Parameters:

  • callback (function) - Called when variables change
    • Receives: variables (object) - Variables object with IDs as keys

Returns: function - Unsubscribe function

Example:

Domo.onVariablesUpdated((variables) => {
  console.log('Variables updated:', variables);

  // Access specific variable by ID
  const themeVariable = variables['391'];
  if (themeVariable) {
    const theme = themeVariable.parsedExpression.value;
    setTheme(theme);
  }
});

Variables Object Structure:

{
  "391": {
    "parsedExpression": {
      "exprType": "NUMERIC_VALUE",
      "value": "9"
    }
  },
  "392": {
    "parsedExpression": {
      "exprType": "STRING_VALUE",
      "value": "dark"
    }
  }
}

Note: Variable IDs (like "391") are defined by Domo. Inspect the variables object in your app to find the correct IDs.


Domo.onAppDataUpdated(callback)

Listen for custom app data updates.

Parameters:

  • callback (function) - Called when app data is received
    • Receives: data (any) - Custom data object

Returns: function - Unsubscribe function

Example:

Domo.onAppDataUpdated((data) => {
  console.log('Received app data:', data);

  // Handle custom data from other apps
  if (data.action === 'highlight') {
    highlightRow(data.rowId);
  }
});

Use Case: Enable communication between multiple custom apps on the same Domo page.


Emitters

Emitters send messages to the parent Domo platform to trigger actions or update state.

Domo.requestFiltersUpdate(filters, pageStateUpdate?, onAck?, onReply?)

Update page-level filters programmatically.

Parameters:

  • filters (Filter[]) - Array of filter objects
  • pageStateUpdate (boolean, optional) - Optional boolean indicating if the page state should be updated by the filter. When false, on the card level filter state will be updated. (default: true)
  • onAck (function, optional) - Called when request is acknowledged
  • onReply (function, optional) - Called when request completes
    • Receives: response (any) - Response data

Returns: string - Request ID for tracking

Example:

// Basic usage
Domo.requestFiltersUpdate([
  {
    column: 'category',
    operator: 'IN',
    values: ['ALERT', 'WARNING'],
    dataType: 'STRING'
  },
  {
    column: 'amount',
    operator: 'GREATER_THAN',
    values: [1000],
    dataType: 'NUMERIC'
  }
]);

With Callbacks:

const requestId = Domo.requestFiltersUpdate(
  filters,
  true,
  () => console.log('Filter update acknowledged'),
  (response) => console.log('Filter update completed:', response)
);

console.log('Request ID:', requestId);

Filter Requirements:

All filter objects must include:

  • column (string, required) - Column name to filter on
  • operator (string, required) - Filter operator (see supported operators above)
  • values (array, required) - Values to filter by
  • dataType (string, required) - Data type: "STRING", "NUMERIC", "DATE", or "DATETIME"

Domo.requestVariablesUpdate(variables, onAck?, onReply?)

Update page variables programmatically.

Parameters:

  • variables (Variable[]) - Array of variable objects
  • onAck (function, optional) - Called when request is acknowledged
  • onReply (function, optional) - Called when request completes

Returns: string - Request ID for tracking

Example:

Domo.requestVariablesUpdate([
  {
    functionId: 123,
    value: 100
  },
  {
    functionId: 124,
    value: 'dark'
  }
]);

Variable Object Structure:

{
  functionId: number,  // Variable function ID from Domo
  value: any          // New value for the variable
}

Domo.requestAppDataUpdate(data, onAck?, onReply?)

Send custom app data to other apps on the same page.

Parameters:

  • data (any) - Custom data object
  • onAck (function, optional) - Called when request is acknowledged
  • onReply (function, optional) - Called when request completes

Returns: void

Example:

// Send custom data
Domo.requestAppDataUpdate({
  action: 'highlight',
  rowId: 123,
  timestamp: Date.now()
});

Use Case: Coordinate interactions between multiple custom apps on the same Domo page.


Navigation

Domo.navigate(url, isNewWindow?)

Navigate to a different page within Domo.

Parameters:

  • url (string) - Domo page URL or route
  • isNewWindow (boolean, optional) - Open in new tab/window (default: false)

Example:

// Navigate to a profile page
Domo.navigate('/profile/3234');

// Open in new tab
Domo.navigate('/page/123456789', true);

Important Notes:

  • Use Domo.navigate() instead of HTML links to change the page hosting the custom app
  • For mobile web platforms, routes are automatically prefixed with /m# (e.g., /m#/profile/3234)
  • External links are restricted to whitelisted domains (configure in Admin > Network Security > Custom Apps authorized domains)

Environment

Domo.env

Access environment information about the current context.

Type: QueryParams (object)

Available Properties:

Domo.env.pageId        // Current page ID
Domo.env.userId        // Current user ID
Domo.env.customer      // Customer name
Domo.env.locale        // Locale (e.g., 'en-US')
Domo.env.environment   // Environment (e.g., 'dev3', 'prod')
Domo.env.platform      // Platform (e.g., 'desktop', 'mobile')

Example:

console.log(`User ${Domo.env.userId} on ${Domo.env.platform}`);

// Conditional logic based on platform
if (Domo.env.platform === 'mobile') {
  renderMobileLayout();
} else {
  renderDesktopLayout();
}

Security Warning: These properties come from URL query parameters and can be spoofed. For secure user identification, always verify with the API:

const authenticatedUser = await Domo.get('/domo/environment/v1/');
console.log('Verified user:', authenticatedUser);

Utilities

Domo.extend(overrides)

Extend or override static methods and properties of the Domo class.

Parameters:

  • overrides (object) - Object with methods/properties to override

Returns: void

Example:

import Domo, { get as originalGet } from 'ryuu.js';

// Add logging to all GET requests
Domo.extend({
  get: async function(url, options) {
    console.log(`[API] Fetching: ${url}`);
    const startTime = Date.now();

    try {
      const result = await originalGet.call(this, url, options);
      console.log(`[API] Success: ${url} (${Date.now() - startTime}ms)`);
      return result;
    } catch (error) {
      console.error(`[API] Error: ${url} (${Date.now() - startTime}ms)`, error);
      throw error;
    }
  }
});

// Now all Domo.get() calls include logging
const data = await Domo.get('/data/v1/sales');

Use Cases:

  • Add logging to all requests
  • Implement retry logic
  • Add caching layer
  • Mock responses for testing
  • Add custom error handling

Domo.getRequests()

Get all tracked requests (ASK-ACK-REPLY pattern).

Returns: AskReplyMap - Object with request IDs as keys

Example:

const requestId = Domo.requestFiltersUpdate(filters);

// Check request status
const requests = Domo.getRequests();
console.log(requests[requestId]);
// {
//   request: { status: 'SENT', timestamp: 1234567890 },
//   response: { status: 'SUCCESS', timestamp: 1234567900, data: {...} }
// }

Domo.getRequest(id)

Get a specific tracked request by ID.

Parameters:

  • id (string) - Request ID

Returns: Request object or undefined

Example:

const requestId = Domo.requestFiltersUpdate(filters);
const request = Domo.getRequest(requestId);
console.log(request.request.status); // 'PENDING', 'SENT'

Domo.__util

Internal utilities exposed for advanced use cases.

Available Utilities:

Domo.__util.isSuccess(statusCode)         // Check if HTTP status is success
Domo.__util.isVerifiedOrigin(origin)      // Verify origin is trusted Domo domain
Domo.__util.getQueryParams()              // Get current query parameters
Domo.__util.setFormatHeaders(format, headers) // Set Accept headers
Domo.__util.generateUniqueId()            // Generate unique request ID
Domo.__util.isIOS()                       // Check if running on iOS

Note: These are internal utilities and may change between versions. Use at your own risk.


TypeScript Support

ryuu.js includes comprehensive TypeScript definitions for a better development experience.

Importing Types

import Domo, {
  // Interfaces
  Filter,
  Variable,
  RequestOptions,
  ObjectResponseBody,
  ArrayResponseBody,
  QueryParams,

  // Enums
  DataFormats,
  DomoDataTypes,
  RequestMethods,

  // Type Guards
  isFilter,
  isFilterArray,
  isVariable,
  isVariableArray,

  // Error Types
  DomoHttpError,
  DomoAuthError,
  DomoConnectionError,
  DomoValidationError,
  DomoTimeoutError,
} from 'ryuu.js';

Typed Requests

// Type-safe request options
const options: RequestOptions<'array-of-objects'> = {
  format: 'array-of-objects',
  query: { limit: 100 }
};

// Return type is automatically inferred
const data: ObjectResponseBody[] = await Domo.get('/data/v1/sales', options);

// Format-specific types
const csv: string = await Domo.get('/data/v1/sales', { format: 'csv' });
const arrays: ArrayResponseBody = await Domo.get('/data/v1/sales', { format: 'array-of-arrays' });
const excel: Blob = await Domo.get('/data/v1/sales', { format: 'excel' });

Typed Filters

const filters: Filter[] = [
  {
    column: 'category',
    operator: 'IN',
    values: ['ALERT', 'WARNING'],
    dataType: 'STRING'
  },
  {
    column: 'amount',
    operator: 'GREATER_THAN',
    values: [1000],
    dataType: 'NUMERIC'
  }
];

Domo.requestFiltersUpdate(filters);

Typed Variables

const variables: Variable[] = [
  { functionId: 123, value: 100 },
  { functionId: 124, value: 'dark' }
];

Domo.requestVariablesUpdate(variables);

Custom Types

// Define your data shape
interface SalesRecord {
  id: number;
  amount: number;
  date: string;
  category: string;
}

// Type-safe data access
const sales = await Domo.get('/data/v1/sales') as SalesRecord[];

sales.forEach(record => {
  console.log(`Sale ${record.id}: $${record.amount}`);
});

Mobile Platform Support

ryuu.js provides full support for iOS and Android mobile platforms through platform-specific APIs.

iOS Integration

On iOS, ryuu.js uses webkit.messageHandlers for native communication:

// Automatically handled by ryuu.js
Domo.requestFiltersUpdate(filters);
// Internally uses: webkit.messageHandlers.domofilter.postMessage()

Domo.requestVariablesUpdate(variables);
// Internally uses: webkit.messageHandlers.domovariable.postMessage()

Android/Flutter Integration

On Android, ryuu.js uses global objects injected by the native app:

// Automatically handled by ryuu.js
Domo.requestVariablesUpdate(variables);
// Internally uses: window.domovariable.postMessage()

Domo.requestFiltersUpdate(filters);
// Internally uses: window.domofilter.postMessage()

Platform Detection

ryuu.js automatically detects the platform and uses the appropriate communication method:

// Check if running on mobile
if (Domo.env.platform === 'mobile') {
  // Mobile-specific logic
  renderMobileView();
} else {
  // Desktop-specific logic
  renderDesktopView();
}

// Internal iOS detection (exposed via __util)
if (Domo.__util.isIOS()) {
  // iOS-specific logic
}

Mobile Considerations

  1. Navigation: Routes are automatically prefixed with /m# on mobile web
  2. Touch Events: Consider touch-friendly UI for mobile apps
  3. Performance: Mobile devices may have limited resources - optimize data fetching
  4. Testing: Test on actual devices or simulators for best results

Error Handling

All HTTP methods return Promises. ryuu.js throws structured, typed error classes that support instanceof checks.

Error Types

Error Class Thrown When Properties
DomoHttpError Non-2xx HTTP response status, statusText, body, headers
DomoAuthError 401 or 403 response (extends DomoHttpError) Same as above
DomoConnectionError Network failure (fetch rejects) message
DomoValidationError Invalid filters/variables, schema parse failure message, errors[]
DomoTimeoutError Request or message timeout message, url

Basic Error Handling

try {
  const data = await Domo.get('/data/v1/sales');
  console.log('Data loaded:', data);
} catch (error) {
  console.error('Failed to load data:', error);
}

Using instanceof for Error Types

import Domo, { DomoHttpError, DomoAuthError, DomoConnectionError } from 'ryuu.js';

try {
  const data = await Domo.get('/data/v1/sales');
} catch (error) {
  if (error instanceof DomoAuthError) {
    // 401 or 403 — session expired or no permission
    console.error('Auth failed:', error.status); // 401 or 403
    showLoginPrompt();
  } else if (error instanceof DomoHttpError) {
    // Any other non-2xx response
    console.error('HTTP error:', error.status, error.statusText);
    console.error('Response body:', error.body);
    console.error('Response headers:', error.headers);
  } else if (error instanceof DomoConnectionError) {
    // Network failure — no response at all
    console.error('Network error:', error.message);
    showOfflineMessage();
  } else {
    console.error('Unexpected error:', error);
  }
}

Validation Errors

When you pass malformed data to filters or variables, ryuu.js throws a DomoValidationError and logs the expected data model to console.error:

import { DomoValidationError } from 'ryuu.js';

try {
  Domo.requestFiltersUpdate([{ bad: 'data' }]);
} catch (error) {
  if (error instanceof DomoValidationError) {
    console.error(error.message); // "All filters must be valid Filter objects."
    console.error(error.errors);  // Array of the invalid items
    // console.error also shows the expected object shape automatically
  }
}

Retry Logic

Implement retry logic for transient errors:

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await Domo.get(url, options);
    } catch (error) {
      if (i === maxRetries - 1) throw error;

      // Only retry on server errors
      if (error.status >= 500) {
        console.log(`Retry ${i + 1}/${maxRetries}...`);
        await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
      } else {
        throw error;
      }
    }
  }
}

// Usage
try {
  const data = await fetchWithRetry('/data/v1/sales');
} catch (error) {
  console.error('Failed after retries:', error);
}

Advanced Usage

Schema Validation

Validate API responses at runtime using any schema library with a .parse() method (zod, valibot, etc.):

import Domo from 'ryuu.js';
import { z } from 'zod';

const UserSchema = z.array(z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
}));

// Throws DomoValidationError if response doesn't match
const users = await Domo.get<z.infer<typeof UserSchema>>('/data/v1/users', {
  schema: UserSchema,
});

Zero cost when schema is not provided — a single falsy check.


Request Interceptors

Register middleware that wraps every HTTP request. Interceptors use an onion model — the last registered runs outermost.

// Logging interceptor
const removeLogger = Domo.intercept(async (config, next) => {
  console.log(`[${config.method}] ${config.url}`);
  const start = Date.now();
  const response = await next(config);
  console.log(`[${config.method}] ${config.url} - ${Date.now() - start}ms`);
  return response;
});

// Add custom headers
Domo.intercept((config, next) => {
  config.headers['X-Custom'] = 'value';
  return next(config);
});

// Short-circuit with a cached response
Domo.intercept((config, next) => {
  if (cache.has(config.url)) return Promise.resolve(cache.get(config.url));
  return next(config);
});

// Remove an interceptor
removeLogger();

The config object has { method, url, headers, body }. Headers and auth tokens are already set when interceptors run.


Debug Mode

Enable category-based debug logging to trace HTTP requests, MessageChannel messages, filter and variable updates:

// Enable all categories
Domo.debug.enable();

// Enable specific categories: 'http', 'messages', 'filters', 'variables', 'all'
Domo.debug.enable(['http', 'messages']);

// Disable
Domo.debug.disable();

Debug state persists to localStorage — it survives page reloads. You can also enable it from the browser console:

localStorage.__domo_debug__ = '["all"]';
// Refresh the page — debug logging is now active

Output looks like:

[domo:http] GET /data/v1/sales
[domo:http] 200 OK /data/v1/sales
[domo:messages] received filtersUpdated { event: 'filtersUpdated', filters: [...] }
[domo:filters] filtersUpdated [{ column: 'region', ... }]

Custom Fetch Implementation

Provide your own fetch implementation for testing or custom behavior:

const data = await Domo.get('/data/v1/sales', {
  fetch: myCustomFetch,
});

Type Guards

Use type guards to validate runtime data:

import { isFilter, isFilterArray, guardAgainstInvalidFilters, DomoValidationError } from 'ryuu.js';

// Validate individual filter
if (isFilter(data)) {
  console.log(data.column); // TypeScript knows data is a Filter
}

// Validate array — throws DomoValidationError with expected model logged to console
try {
  guardAgainstInvalidFilters(data);
} catch (error) {
  if (error instanceof DomoValidationError) {
    console.error(error.errors); // The invalid items
  }
}

Complete Example

Here's a comprehensive example showing a real-world custom app:

import Domo from 'ryuu.js';

class SalesDashboard {
  constructor() {
    this.data = [];
    this.filters = [];
    this.initialize();
  }

  async initialize() {
    // Set up event listeners
    this.setupEventListeners();

    // Load initial data
    await this.loadData();

    // Render dashboard
    this.render();
  }

  setupEventListeners() {
    // Listen for dataset updates
    Domo.onDataUpdated((datasetAlias) => {
      console.log(`Dataset ${datasetAlias} updated`);
      this.loadData();
    });

    // Listen for filter changes
    Domo.onFiltersUpdated((filters) => {
      console.log('Filters updated:', filters);
      this.filters = filters;
      this.applyFilters();
    });

    // Listen for variable changes
    Domo.onVariablesUpdated((variables) => {
      console.log('Variables updated:', variables);
      this.updateTheme(variables);
    });

    // Set up UI event handlers
    document.getElementById('refreshBtn').addEventListener('click', () => {
      this.loadData();
    });

    document.getElementById('exportBtn').addEventListener('click', () => {
      this.exportData();
    });

    document.getElementById('filterBtn').addEventListener('click', () => {
      this.updateFilters();
    });
  }

  async loadData() {
    try {
      // Load multiple datasets in parallel
      const [sales, customers, products] = await Domo.getAll([
        '/data/v1/sales',
        '/data/v1/customers',
        '/data/v1/products'
      ]);

      this.data = {
        sales,
        customers,
        products
      };

      this.render();
    } catch (error) {
      console.error('Failed to load data:', error);
      this.showError('Unable to load dashboard data');
    }
  }

  applyFilters() {
    // Find category filter
    const categoryFilter = this.filters.find(f => f.column === 'category');

    if (categoryFilter && categoryFilter.values.length > 0) {
      // Filter local data
      const filteredSales = this.data.sales.filter(sale =>
        categoryFilter.values.includes(sale.category)
      );

      this.renderSales(filteredSales);
    } else {
      this.renderSales(this.data.sales);
    }
  }

  updateFilters() {
    // Get selected categories from UI
    const selectedCategories = Array.from(
      document.querySelectorAll('.category-checkbox:checked')
    ).map(cb => cb.value);

    // Update page filters
    Domo.requestFiltersUpdate(
      [
        {
          column: 'category',
          operator: 'IN',
          values: selectedCategories,
          dataType: 'STRING'
        }
      ],
      true,
      () => console.log('Filter update acknowledged'),
      (response) => console.log('Filter update completed:', response)
    );
  }

  async exportData() {
    try {
      // Export as CSV
      const csv = await Domo.get('/data/v1/sales', { format: 'csv' });

      // Download file
      const blob = new Blob([csv], { type: 'text/csv' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `sales-export-${Date.now()}.csv`;
      a.click();
      URL.revokeObjectURL(url);

      console.log('Export successful');
    } catch (error) {
      console.error('Export failed:', error);
      this.showError('Failed to export data');
    }
  }

  updateTheme(variables) {
    // Check for theme variable (example ID: 391)
    const themeVar = variables['391'];

    if (themeVar) {
      const theme = themeVar.parsedExpression.value;
      document.body.className = `theme-${theme}`;
    }
  }

  render() {
    if (!this.data.sales) return;

    // Render sales chart
    this.renderSales(this.data.sales);

    // Render customer stats
    this.renderCustomerStats(this.data.customers);

    // Render product list
    this.renderProducts(this.data.products);
  }

  renderSales(sales) {
    const salesContainer = document.getElementById('salesChart');

    // Calculate totals
    const total = sales.reduce((sum, sale) => sum + sale.amount, 0);
    const count = sales.length;
    const average = total / count;

    salesContainer.innerHTML = `
      <div class="stats">
        <div class="stat">
          <h3>Total Sales</h3>
          <p>$${total.toLocaleString()}</p>
        </div>
        <div class="stat">
          <h3>Number of Sales</h3>
          <p>${count.toLocaleString()}</p>
        </div>
        <div class="stat">
          <h3>Average Sale</h3>
          <p>$${average.toFixed(2)}</p>
        </div>
      </div>
    `;

    // Render chart (using your charting library)
    // this.renderChart(sales);
  }

  renderCustomerStats(customers) {
    const customerContainer = document.getElementById('customerStats');

    customerContainer.innerHTML = `
      <h3>Total Customers: ${customers.length}</h3>
      <ul>
        ${customers.slice(0, 10).map(c => `
          <li>${c.name} - ${c.email}</li>
        `).join('')}
      </ul>
    `;
  }

  renderProducts(products) {
    const productContainer = document.getElementById('productList');

    productContainer.innerHTML = `
      <table>
        <thead>
          <tr>
            <th>Product</th>
            <th>Price</th>
            <th>Stock</th>
          </tr>
        </thead>
        <tbody>
          ${products.map(p => `
            <tr>
              <td>${p.name}</td>
              <td>$${p.price}</td>
              <td>${p.stock}</td>
            </tr>
          `).join('')}
        </tbody>
      </table>
    `;
  }

  showError(message) {
    const errorContainer = document.getElementById('error');
    errorContainer.textContent = message;
    errorContainer.style.display = 'block';

    setTimeout(() => {
      errorContainer.style.display = 'none';
    }, 5000);
  }
}

// Initialize app
new SalesDashboard();

Breaking Changes (v6.0)

Domo.env type changes

Domo.env previously returned raw query parameters (QueryParams with string | number | undefined values). It now returns a typed DomoEnv object:

  • userId, userName, userEmail, customer, locale, pageId are now always string (previously could be number or undefined)
  • New properties: host (from environment API), loaded (boolean indicating if the API fetch completed)
  • platform is now typed as "desktop" | "mobile" (defaults to "desktop")
  • A background GET /domo/environment/v1 request is made on initialization to enrich the environment. This is new network traffic that fails silently if the endpoint is unavailable.

Migration: If your code checks typeof Domo.env.userId === 'number', update it to treat userId as a string. All other existing Domo.env.* usage is backwards compatible.


Migration Guide

Deprecated Methods

The following methods have been renamed for consistency. Old methods still work but are deprecated and will be removed in a future version.

Deprecated Method New Method Description
Domo.onDataUpdate Domo.onDataUpdated Listen for dataset changes
Domo.onFiltersUpdate Domo.onFiltersUpdated Listen for filter changes
Domo.onAppData Domo.onAppDataUpdated Listen for app data changes
Domo.filterContainer Domo.requestFiltersUpdate Set page filters
Domo.sendVariables Domo.requestVariablesUpdate Update page variables
Domo.sendAppData Domo.requestAppDataUpdate Send custom app data

Migration Steps

  1. Find and Replace:
// Old
Domo.onDataUpdate((alias) => { ... });
Domo.onFiltersUpdate((filters) => { ... });
Domo.onAppData((data) => { ... });
Domo.filterContainer(filters);
Domo.sendVariables(variables);
Domo.sendAppData(data);

// New
Domo.onDataUpdated((alias) => { ... });
Domo.onFiltersUpdated((filters) => { ... });
Domo.onAppDataUpdated((data) => { ... });
Domo.requestFiltersUpdate(filters);
Domo.requestVariablesUpdate(variables);
Domo.requestAppDataUpdate(data);
  1. Update Dependencies:
npm update ryuu.js
  1. Test Thoroughly:

After migration, test all functionality to ensure everything works correctly.


Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Make your changes
  4. Add tests for new functionality
  5. Run tests: npm test
  6. Build: npm run build
  7. Commit: git commit -m "Add my feature"
  8. Push: git push origin feature/my-feature
  9. Open a Pull Request

Development Setup

# Clone repository
git clone https://github.com/your-org/domo.js.git
cd domo.js

# Install dependencies
npm install

# Run tests
npm test

# Run tests with coverage
npm run coverage

# Build
npm run build

# Start demo server
npm run demo

License

SEE LICENSE IN LICENSE

Copyright (c) Domo


Support

For issues, questions, or contributions:

  • Issues: Open an issue on GitHub
  • Documentation: See CLAUDE.md for developer documentation
  • Domo Developer Portal: Visit the Domo developer documentation

Changelog

See CHANGELOG.md for version history and changes.


v4 and Earlier Documentation

Note: This documentation is for ryuu.js v4.x and earlier. For the latest version, see the v6+ Documentation.

Table of Contents


Quick Start

Get started with ryuu.js in just a few lines:

import domo from 'ryuu.js';

// Fetch data from a dataset
const data = await domo.get('/data/v1/sales');
console.log(data); // Array of objects with your data

// Listen for dataset updates
domo.onDataUpdate((alias) => {
  console.log(`Dataset ${alias} was updated!`);
  // Refresh your visualization
});

// Listen for filter changes
domo.onFiltersUpdate((filters) => {
  console.log('Filters changed:', filters);
  // Apply filters to your data
});

Features

  • HTTP API Access - Authenticated requests to Domo datasets, datastores, and APIs
  • Real-time Events - Listen for dataset updates, filter changes, and variable updates
  • Filter Management - Get and set page-level filters programmatically
  • Variable Management - Access and update page variables
  • Custom App Data - Send custom data between apps on the same page
  • Mobile Support - iOS and Android compatibility
  • TypeScript Ready - Type definitions included
  • Zero Dependencies - Minimal bundle size

Installation

NPM

npm install ryuu.js@4

Then import in your application:

// ES modules
import domo from 'ryuu.js';

// CommonJS
const domo = require('ryuu.js');

CDN / Script Tag

<script src="https://unpkg.com/ryuu.js@4"></script>
<script>
  // domo is available globally
  domo.get('/data/v1/sales').then(data => {
    console.log(data);
  });
</script>

TypeScript

TypeScript definitions are included automatically:

import domo, { Filter, Variable, RequestOptions } from 'ryuu.js';

Core Concepts

Communication Architecture

Your custom app runs in an iframe within the Domo platform. ryuu.js v4 establishes bidirectional communication using:

  • window.postMessage API - Primary communication mechanism for desktop/web
  • webkit.messageHandlers - iOS native integration
  • Global objects - Android integration

Environment Context

Access information about the current user and environment via domo.env:

console.log(domo.env.userId);      // Current user ID
console.log(domo.env.customer);    // Customer name
console.log(domo.env.pageId);      // Current page ID
console.log(domo.env.locale);      // Locale (e.g., 'en-US')
console.log(domo.env.platform);    // Platform (e.g., 'desktop', 'mobile')

Security Note: Environment properties come from URL parameters and can be spoofed. Always verify with the API for secure operations:

const authenticatedUser = await domo.get('/domo/environment/v1/');

API Reference

HTTP Methods

All HTTP methods return Promises and support multiple data formats.

domo.get(url, options?)

Fetch data from a Domo dataset or API endpoint.

Parameters:

  • url (string) - API endpoint URL
  • options (object, optional) - Request options

Returns: Promise<ResponseBody>

Basic Usage:

// Returns array of objects by default
const data = await domo.get('/data/v1/sales');
console.log(data); // [{ id: 1, amount: 100, ... }, ...]

Format Options:

// CSV format
const csv = await domo.get('/data/v1/sales', { format: 'csv' });

// Array of arrays with metadata
const arrayData = await domo.get('/data/v1/sales', { format: 'array-of-arrays' });
console.log(arrayData.columns);  // ['id', 'amount', 'date']
console.log(arrayData.rows);     // [[1, 100, '2024-01-01'], ...]

// Excel format (returns Blob)
const excel = await domo.get('/data/v1/sales', { format: 'excel' });

Supported Formats:

  • 'array-of-objects' (default) - Returns ObjectResponseBody[]
  • 'array-of-arrays' - Returns ArrayResponseBody with metadata
  • 'csv' - Returns CSV string
  • 'excel' - Returns Excel Blob
  • 'plain' - Returns plain text string

Query Parameters:

const data = await domo.get('/data/v1/sales', {
  query: {
    limit: 100,
    offset: 0,
    fields: 'id,amount,date'
  }
});

domo.getAll(urls, options?)

Fetch multiple datasets or endpoints in parallel.

Parameters:

  • urls (string[]) - Array of API endpoint URLs
  • options (object, optional) - Request options applied to all requests

Returns: Promise<ResponseBody[]>

Example:

const [sales, inventory, customers] = await domo.getAll([
  '/data/v1/sales',
  '/data/v1/inventory',
  '/data/v1/customers'
]);

domo.post(url, body?, options?)

Send a POST request to create data.

Parameters:

  • url (string) - API endpoint URL
  • body (object | string, optional) - Request body
  • options (object, optional) - Request options

Returns: Promise<ResponseBody>

Example:

// Create a document in Domo DataStore
const result = await domo.post(
  '/domo/datastores/v1/collections/users/documents/',
  {
    name: 'John Doe',
    email: 'john@example.com',
    role: 'Admin'
  }
);

domo.put(url, body?, options?)

Send a PUT request to update data.

Example:

// Update an existing document
const result = await domo.put(
  '/domo/datastores/v1/collections/users/documents/abc123',
  {
    name: 'Jane Doe',
    role: 'Manager'
  }
);

domo.delete(url, options?)

Send a DELETE request to remove data.

Example:

// Delete a document
await domo.delete('/domo/datastores/v1/collections/users/documents/abc123');

Event Listeners

Event listeners enable real-time reactivity to changes in the Domo platform. All listener methods return an unsubscribe function.

domo.onDataUpdate(callback)

Listen for dataset update events.

Parameters:

  • callback (function) - Called when a dataset is updated
    • Receives: datasetAlias (string) - Alias of the updated dataset

Returns: function - Unsubscribe function

Example:

const unsubscribe = domo.onDataUpdate((datasetAlias) => {
  console.log(`Dataset ${datasetAlias} was updated`);
  // Reload data for this dataset
  loadData();
});

// Later, stop listening
unsubscribe();

domo.onFiltersUpdate(callback)

Listen for page filter changes.

Parameters:

  • callback (function) - Called when filters change
    • Receives: filters (Filter[]) - Array of filter objects

Returns: function - Unsubscribe function

Example:

domo.onFiltersUpdate((filters) => {
  console.log('Filters updated:', filters);

  // Find specific filter
  const categoryFilter = filters.find(f => f.column === 'category');

  if (categoryFilter) {
    console.log('Category filter:', categoryFilter.values);
    // Apply filter to your visualization
    applyFilters(categoryFilter.values);
  }
});

Filter Object Structure:

{
  column: "category",              // Column name being filtered
  operator: "IN",                  // Filter operator
  values: ["ALERT", "WARNING"],    // Array of filter values
  dataType: "STRING",              // Data type: STRING, NUMERIC, DATE, DATETIME
  dataSourceId: "46d91556-...",    // Source dataset ID (optional)
  label: "category"                // Display label (optional)
}

Supported Operators:

String Operators:

  • "IN" / "NOT_IN"
  • "CONTAINS" / "NOT_CONTAINS"
  • "STARTS_WITH" / "NOT_STARTS_WITH"
  • "ENDS_WITH" / "NOT_ENDS_WITH"

Numeric/Date Operators:

  • "EQUALS" / "NOT_EQUALS"
  • "GREATER_THAN" / "GREAT_THAN_EQUALS_TO"
  • "LESS_THAN" / "LESS_THAN_EQUALS_TO"
  • "BETWEEN"

domo.onAppData(callback)

Listen for custom app data updates.

Parameters:

  • callback (function) - Called when app data is received
    • Receives: data (any) - Custom data object

Returns: function - Unsubscribe function

Example:

domo.onAppData((data) => {
  console.log('Received app data:', data);

  // Handle custom data from other apps
  if (data.action === 'highlight') {
    highlightRow(data.rowId);
  }
});

Emitters

Emitters send messages to the parent Domo platform to trigger actions or update state.

domo.filterContainer(filters, pageStateUpdate?)

Update page-level filters programmatically.

Parameters:

  • filters (Filter[]) - Array of filter objects
  • pageStateUpdate (boolean, optional) - Whether to update page state (default: true)

Returns: void

Example:

// Basic usage
domo.filterContainer([
  {
    column: 'category',
    operator: 'IN',
    values: ['ALERT', 'WARNING'],
    dataType: 'STRING'
  },
  {
    column: 'amount',
    operator: 'GREATER_THAN',
    values: [1000],
    dataType: 'NUMERIC'
  }
]);

domo.sendVariables(variables)

Update page variables programmatically.

Parameters:

  • variables (Variable[]) - Array of variable objects

Returns: void

Example:

domo.sendVariables([
  {
    functionId: 123,
    value: 100
  },
  {
    functionId: 124,
    value: 'dark'
  }
]);

Variable Object Structure:

{
  functionId: number,  // Variable function ID from Domo
  value: any          // New value for the variable
}

domo.sendAppData(data)

Send custom app data to other apps on the same page.

Parameters:

  • data (any) - Custom data object

Returns: void

Example:

// Send custom data
domo.sendAppData({
  action: 'highlight',
  rowId: 123,
  timestamp: Date.now()
});

Navigation

Navigation in v4 is available through the parent window. For programmatic navigation, you can use:

// Navigate within Domo
window.parent.postMessage({
  event: 'navigate',
  data: { route: '/page/123456789' }
}, '*');

Environment

domo.env

Access environment information about the current context.

Type: QueryParams (object)

Available Properties:

domo.env.pageId        // Current page ID
domo.env.userId        // Current user ID
domo.env.customer      // Customer name
domo.env.locale        // Locale (e.g., 'en-US')
domo.env.environment   // Environment (e.g., 'dev3', 'prod')
domo.env.platform      // Platform (e.g., 'desktop', 'mobile')

TypeScript Support

ryuu.js v4 includes TypeScript definitions.

Importing Types

import domo, {
  // Interfaces
  Filter,
  Variable,
  RequestOptions,
  ObjectResponseBody,
  ArrayResponseBody,
  QueryParams,

  // Enums
  DataFormats,
  DomoDataTypes,
  RequestMethods
} from 'ryuu.js';

Typed Requests

// Type-safe request options
const options: RequestOptions<'array-of-objects'> = {
  format: 'array-of-objects',
  query: { limit: 100 }
};

// Return type is automatically inferred
const data: ObjectResponseBody[] = await domo.get('/data/v1/sales', options);

// Format-specific types
const csv: string = await domo.get('/data/v1/sales', { format: 'csv' });
const arrays: ArrayResponseBody = await domo.get('/data/v1/sales', { format: 'array-of-arrays' });

Typed Filters

const filters: Filter[] = [
  {
    column: 'category',
    operator: 'IN',
    values: ['ALERT', 'WARNING'],
    dataType: 'STRING'
  }
];

domo.filterContainer(filters);

Mobile Platform Support

ryuu.js v4 provides support for iOS and Android mobile platforms.

iOS Integration

On iOS, ryuu.js uses webkit.messageHandlers for native communication:

// Automatically handled by ryuu.js
domo.filterContainer(filters);
// Internally uses: webkit.messageHandlers.domofilter.postMessage()

domo.sendVariables(variables);
// Internally uses: webkit.messageHandlers.domovariable.postMessage()

Android Integration

On Android, ryuu.js uses global objects injected by the native app:

// Automatically handled by ryuu.js
domo.sendVariables(variables);
// Internally uses: window.domovariable.postMessage()

domo.filterContainer(filters);
// Internally uses: window.domofilter.postMessage()

Error Handling

All HTTP methods return Promises. Use try/catch with async/await for error handling.

Basic Error Handling

try {
  const data = await domo.get('/data/v1/sales');
  console.log('Data loaded:', data);
} catch (error) {
  console.error('Failed to load data:', error);
}

Error Object Structure

try {
  const data = await domo.get('/data/v1/nonexistent');
} catch (error) {
  console.error('Status:', error.status);         // 404
  console.error('Status Text:', error.statusText); // 'Not Found'
  console.error('Message:', error.message);        // Error description
}

Handling Specific Error Types

try {
  const data = await domo.get('/data/v1/sales');
} catch (error) {
  if (error.status === 404) {
    console.error('Dataset not found');
  } else if (error.status === 403) {
    console.error('Access denied');
  } else if (error.status === 401) {
    console.error('Authentication failed');
  } else if (error.status >= 500) {
    console.error('Server error');
  }
}

Complete Example

Here's a comprehensive example showing a real-world custom app:

import domo from 'ryuu.js';

class SalesDashboard {
  constructor() {
    this.data = [];
    this.filters = [];
    this.initialize();
  }

  async initialize() {
    // Set up event listeners
    this.setupEventListeners();

    // Load initial data
    await this.loadData();

    // Render dashboard
    this.render();
  }

  setupEventListeners() {
    // Listen for dataset updates
    domo.onDataUpdate((datasetAlias) => {
      console.log(`Dataset ${datasetAlias} updated`);
      this.loadData();
    });

    // Listen for filter changes
    domo.onFiltersUpdate((filters) => {
      console.log('Filters updated:', filters);
      this.filters = filters;
      this.applyFilters();
    });

    // Set up UI event handlers
    document.getElementById('refreshBtn').addEventListener('click', () => {
      this.loadData();
    });

    document.getElementById('exportBtn').addEventListener('click', () => {
      this.exportData();
    });

    document.getElementById('filterBtn').addEventListener('click', () => {
      this.updateFilters();
    });
  }

  async loadData() {
    try {
      // Load multiple datasets in parallel
      const [sales, customers, products] = await domo.getAll([
        '/data/v1/sales',
        '/data/v1/customers',
        '/data/v1/products'
      ]);

      this.data = {
        sales,
        customers,
        products
      };

      this.render();
    } catch (error) {
      console.error('Failed to load data:', error);
      this.showError('Unable to load dashboard data');
    }
  }

  applyFilters() {
    // Find category filter
    const categoryFilter = this.filters.find(f => f.column === 'category');

    if (categoryFilter && categoryFilter.values.length > 0) {
      // Filter local data
      const filteredSales = this.data.sales.filter(sale =>
        categoryFilter.values.includes(sale.category)
      );

      this.renderSales(filteredSales);
    } else {
      this.renderSales(this.data.sales);
    }
  }

  updateFilters() {
    // Get selected categories from UI
    const selectedCategories = Array.from(
      document.querySelectorAll('.category-checkbox:checked')
    ).map(cb => cb.value);

    // Update page filters
    domo.filterContainer([
      {
        column: 'category',
        operator: 'IN',
        values: selectedCategories,
        dataType: 'STRING'
      }
    ]);
  }

  async exportData() {
    try {
      // Export as CSV
      const csv = await domo.get('/data/v1/sales', { format: 'csv' });

      // Download file
      const blob = new Blob([csv], { type: 'text/csv' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `sales-export-${Date.now()}.csv`;
      a.click();
      URL.revokeObjectURL(url);

      console.log('Export successful');
    } catch (error) {
      console.error('Export failed:', error);
      this.showError('Failed to export data');
    }
  }

  render() {
    if (!this.data.sales) return;

    // Render sales chart
    this.renderSales(this.data.sales);

    // Render customer stats
    this.renderCustomerStats(this.data.customers);

    // Render product list
    this.renderProducts(this.data.products);
  }

  renderSales(sales) {
    const salesContainer = document.getElementById('salesChart');

    // Calculate totals
    const total = sales.reduce((sum, sale) => sum + sale.amount, 0);
    const count = sales.length;
    const average = total / count;

    salesContainer.innerHTML = `
      <div class="stats">
        <div class="stat">
          <h3>Total Sales</h3>
          <p>$${total.toLocaleString()}</p>
        </div>
        <div class="stat">
          <h3>Number of Sales</h3>
          <p>${count.toLocaleString()}</p>
        </div>
        <div class="stat">
          <h3>Average Sale</h3>
          <p>$${average.toFixed(2)}</p>
        </div>
      </div>
    `;
  }

  renderCustomerStats(customers) {
    const customerContainer = document.getElementById('customerStats');

    customerContainer.innerHTML = `
      <h3>Total Customers: ${customers.length}</h3>
      <ul>
        ${customers.slice(0, 10).map(c => `
          <li>${c.name} - ${c.email}</li>
        `).join('')}
      </ul>
    `;
  }

  renderProducts(products) {
    const productContainer = document.getElementById('productList');

    productContainer.innerHTML = `
      <table>
        <thead>
          <tr>
            <th>Product</th>
            <th>Price</th>
            <th>Stock</th>
          </tr>
        </thead>
        <tbody>
          ${products.map(p => `
            <tr>
              <td>${p.name}</td>
              <td>$${p.price}</td>
              <td>${p.stock}</td>
            </tr>
          `).join('')}
        </tbody>
      </table>
    `;
  }

  showError(message) {
    const errorContainer = document.getElementById('error');
    errorContainer.textContent = message;
    errorContainer.style.display = 'block';

    setTimeout(() => {
      errorContainer.style.display = 'none';
    }, 5000);
  }
}

// Initialize app
new SalesDashboard();

Upgrading to v6+

If you're using v4 and want to upgrade to the latest v6+, here's what you need to know.

Breaking Changes

v5 introduced method renames for consistency. All old method names are still supported as deprecated aliases, so your code will continue to work.

Method Name Changes

v4 Method v5 Method Description
domo.onDataUpdate Domo.onDataUpdated Listen for dataset changes
domo.onFiltersUpdate Domo.onFiltersUpdated Listen for filter changes
domo.onAppData Domo.onAppDataUpdated Listen for app data changes
domo.filterContainer Domo.requestFiltersUpdate Set page filters
domo.sendVariables Domo.requestVariablesUpdate Update page variables
domo.sendAppData Domo.requestAppDataUpdate Send custom app data

Class Name Change

The main class was renamed from domo (lowercase) to Domo (PascalCase):

// v4
import domo from 'ryuu.js';
domo.get('/data/v1/sales');

// v5
import Domo from 'ryuu.js';
Domo.get('/data/v1/sales');

New Features in v5

  • Request Tracking - Domo.getRequests() and Domo.getRequest(id) for tracking async operations
  • Navigate Method - Domo.navigate(route, isNewWindow) for programmatic navigation
  • Variables Event - Domo.onVariablesUpdated() listener
  • Extend Method - Domo.extend() for overriding methods
  • Better Mobile Support - Enhanced iOS and Android integration
  • Fetch API - Modern replacement for XMLHttpRequest
  • MessageChannel - More reliable bidirectional communication

Migration Steps

  1. Update Package:

    npm install ryuu.js@latest
  2. Update Imports:

    // Old
    import domo from 'ryuu.js';
    
    // New
    import Domo from 'ryuu.js';
  3. Update Method Names (Optional):

    // Old (still works)
    domo.onDataUpdate((alias) => { ... });
    domo.onFiltersUpdate((filters) => { ... });
    domo.filterContainer(filters);
    
    // New (recommended)
    Domo.onDataUpdated((alias) => { ... });
    Domo.onFiltersUpdated((filters) => { ... });
    Domo.requestFiltersUpdate(filters);
  4. Test Thoroughly - After migration, test all functionality.

For complete v6 documentation, see v6+ Documentation.


Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Make your changes
  4. Add tests for new functionality
  5. Run tests: npm test
  6. Build: npm run build
  7. Commit: git commit -m "Add my feature"
  8. Push: git push origin feature/my-feature
  9. Open a Pull Request

License

SEE LICENSE IN LICENSE

Copyright (c) Domo


Made with ❤️ by the Domo team

About

DomoApps JavaScript Utility Library

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors