Middleware for local Domo App development. It intercepts calls to Domo data/API endpoints and proxies authenticated requests to your Domo instance, so you can develop against real data from your local dev server.
- Intercepts and proxies Domo API traffic:
/data/v{d},/sql/v{d},/dql/v{d},/domo/.../v{d},/api/... - Drop-in Express / Connect middleware via
proxy.express() - Framework-agnostic
proxy.stream(req)for Koa, Nodehttp, Fastify, etc. - Handles multipart/form-data uploads (file streams via
busboy) - Injects OAuth access/refresh tokens for apps using DQL, writeback, or OAuth features
- Reads auth from your existing Domo CLI login session — no extra config
- Ships with TypeScript types
- ESM package
- Node.js 18+
- An active Domo CLI session (
domo login) — see@ryuu - A project
manifest.jsonthat has been published at least once (domo publish) - Peer:
express ^4.17.0 || ^5.0.0(only required if you useproxy.express())
pnpm add -D @domoinc/ryuu-proxy
# or
npm install --save-dev @domoinc/ryuu-proxy
# or
yarn add -D @domoinc/ryuu-proxyThis library leverages the last login session from your Domo CLI. If that session is no longer active or doesn't exist, the proxy won't work. Log in before starting your dev server:
domo loginimport { Proxy } from '@domoinc/ryuu-proxy';
import manifest from './path/to/app/manifest.json' with { type: 'json' };
const proxy = new Proxy({ manifest });The Proxy constructor accepts a config object.
manifest— parsed contents of your project'smanifest.json.domo publishmust have been run at least once so the manifest has anid.
manifest.proxyId— required for apps using DQL, writebacks, or OAuth. If you're unsure, you probably don't need it. See Getting a proxyId.
import express from 'express';
const app = express();
app.use(proxy.express());For other frameworks, stream() returns a readable stream you can pipe back to your response. It accepts a standard Node IncomingMessage, which most frameworks extend.
// koa
app.use(async (ctx, next) => {
await proxy
.stream(ctx.req)
.then((data) => (ctx.body = ctx.req.pipe(data)))
.catch(next);
});// express
app.use((req, res, next) => {
proxy
.stream(req)
.then((stream) => stream.pipe(res))
.catch(() => next());
});// node http
import http from 'node:http';
const server = http.createServer((req, res) => {
if (req.url === '/') {
loadHomePage(res);
} else {
proxy.stream(req).then((stream) => stream.pipe(res));
}
});Ignoring errors causes the proxy to fail silently and the request returns 404. To expose errors, catch DomoException and forward the status:
// koa
app.use(async (ctx, next) => {
await proxy
.stream(ctx.req)
.then((data) => (ctx.body = ctx.req.pipe(data)))
.catch((err) => {
if (err.name === 'DomoException') {
ctx.status = err.status || err.statusCode || 500;
ctx.body = err;
} else {
next();
}
});
});// express / connect
app.use((req, res, next) => {
proxy
.stream(req)
.then((stream) => stream.pipe(res))
.catch((err) => {
if (err.name === 'DomoException') {
res.status(err.status || err.statusCode || 500).json(err);
} else {
next();
}
});
});// node http
const server = http.createServer((req, res) => {
if (req.url === '/') {
loadHomePage();
} else {
proxy
.stream(req)
.then((stream) => stream.pipe(res))
.catch((err) => {
if (err.name === 'DomoException') {
res.writeHead(err.status || err.statusCode || 500);
res.end(JSON.stringify(err));
}
});
}
});DomoException shape:
| Field | Description |
|---|---|
name |
Always "DomoException" |
status / statusCode |
HTTP status code |
statusMessage |
Error description |
If you sit behind a corporate HTTP proxy, set any of the following environment variables. REACT_APP_-prefixed variants are also honored for Create React App projects.
| Variable | Purpose |
|---|---|
PROXY_HOST / REACT_APP_PROXY_HOST |
Proxy hostname |
PROXY_PORT / REACT_APP_PROXY_PORT |
Proxy port |
PROXY_USERNAME / REACT_APP_PROXY_USERNAME |
Basic auth username (optional) |
PROXY_PASSWORD / REACT_APP_PROXY_PASSWORD |
Basic auth password (optional) |
Apps using DQL, writeback, or OAuth features must supply a proxyId in the proxy configuration so the proxy can route requests correctly. A proxyId is of the form XXXXXXXX-XXXX-4XXX-XXXX-XXXXXXXXXXXX. To find it:
- Ensure the app has been published at least once with
domo publish. - Publish a new card from your app design, or open an existing card built from it.
- Right-click the card and choose Inspect element.
- Find the
<iframe>containing your app. Its URL looks like//{HASH}.domoapps.prodX.domo.com?userId=.... - Copy the hash between
//and.domoapps— that is yourproxyId.
proxyIds tie apps to cards. If you delete the card you pulled the ID from, you'll need a new one from another card created from the same app design.
ryuu— the Domo CLI (domo login,domo publish, etc.)ryuu-client— underlying Domo API client
- Create a branch (e.g.
DOMO-XXXXXX) - Make and commit changes
- Test — optionally publish an alpha/beta for integration testing
- Open a pull request
- After merge to
master, bump the version and release to npm
| Script | Description |
|---|---|
pnpm run build |
Compile TypeScript to dist/ |
pnpm test |
Run the test suite (vitest) |
pnpm run test:watch |
Run vitest in watch mode |
pnpm run test:coverage |
Run tests with coverage |
pnpm run format |
Format source with Prettier |
pnpm run release:production |
Build and publish to npm |
pnpm run release:beta |
Build and publish under the beta tag |
pnpm run release:alpha |
Build and publish under the alpha tag |
This project uses standard-version with conventional commits to determine version bumps.