@edynamix/exsto-insights-sdk
v0.1.0-rc.1
Published
Typed SDK for Exsto Insights: queries, aggregates, grain-safe joins, and module registries.
Downloads
117
Maintainers
Readme
@edynamix/exsto-insights-sdk
Typed SDK for the Exsto Insights API: queries, aggregates, grain-safe joins, and a discoverable schema registry. Every table and field is described in TypeScript, so your editor autocompletes table names, field names, and result shapes, and invalid specs fail at compile time instead of at runtime.
Requirements
- Node.js 18 or newer (uses the global
fetch), or any modern browser runtime - ESM only:
importworks everywhere,require()is not supported - TypeScript 5.0+ recommended for the full typed surface (plain JavaScript works too)
Install
npm install @edynamix/exsto-insights-sdkQuickstart
Create an API token in the Exsto Insights dashboard (Settings, admin only), then:
import { createExsto } from '@edynamix/exsto-insights-sdk';
const exsto = createExsto({
url: 'https://your-exsto-insights-host',
token: process.env.EXSTO_TOKEN,
});
const page = await exsto.query('stockVehicles', {
select: ['importDate', 'centerName', 'brand', 'profit'],
where: { importDate: { gte: new Date('2026-06-01') } },
orderBy: { field: 'profit', dir: 'desc' },
limit: 50,
});
for (const row of page.rows) {
// row is exactly { importDate: Date; centerName: string; brand: string; profit: number }
console.log(row.centerName, row.brand, row.profit);
}Field names are always the clean camelCase names from the registry; raw warehouse column
names never appear in specs or results. Date fields arrive as real Date objects.
Aggregate
const byBrand = await exsto.aggregate('stockVehicles', {
groupBy: ['brand'],
aggregate: { totalProfit: { sum: 'profit' }, vehicles: { count: 'id' } },
having: { vehicles: { gte: 5 } },
orderBy: { field: 'totalProfit', dir: 'desc' },
});groupBy also accepts calendar buckets on date fields: 'importDate:day',
'importDate:week', 'importDate:month'.
Join
Joins are grain-safe: the on key must exist in both tables AND cover the joined
table's declared grain, checked at compile time (and asserted again at runtime).
Results are namespaced per table:
const joined = await exsto.join({
from: 'customerReach',
join: [{ with: 'messaging', on: ['periodId'] }],
select: { customerReach: ['periodId', 'emailCount'], messaging: ['smsCount'] },
});
const row = joined.rows[0];
// row.customerReach.emailCount, row.messaging.smsCountDiscover the schema
const schema = exsto.schema();
// Every table with its fields, kinds, grain, and tenancy; safe to serialize for codegen.Modules
The default client is typed against every built-in module. The full catalog is exported from the main entry when you need it directly (narrowed clients, offline tests):
import { BUILTIN_MODULES, BUILTIN_REGISTRY } from '@edynamix/exsto-insights-sdk';
const stockOnly = BUILTIN_MODULES['stock-master'];Configuration
createExsto({
url: 'https://your-exsto-insights-host',
// Machine API token, sent as `Authorization: Bearer <token>`. Omit in the browser:
// the HttpOnly session cookie authenticates same-origin requests instead.
token: '...',
// Optional client-side narrowing; the server only ever narrows further.
centerIds: [1, 2],
// Extra headers for every request; a function is re-read per request.
headers: () => ({ 'x-request-source': 'nightly-report' }),
});Advanced
createExstoFromRegistry(registry, config)builds a client over your own registry subset (e.g. a single module) with the same typed surface.createMemoryTransport(registry, rowsByTable)runs the full query engine in memory over fixture rows, for unit tests without a network.IExstoClient<R>names the client type when you need to pass it around.- Failed HTTP requests throw
Errorwith the status and response body in the message; invalid specs throwExstoValidationError.
License
Apache-2.0. Copyright 2026 eDynamix.
