.md

Quick reference

The SDK calls you use most, on one page. Every option, return value and type is in the API reference.

Install

npm install @dcupl/core @dcupl/loader
yarn add @dcupl/core @dcupl/loader
pnpm add @dcupl/core @dcupl/loader

@dcupl/loader is only needed to load a dcupl.lc.json configuration (see Setup options).

Lifecycle

lifecycle.ts
import { Dcupl } from '@dcupl/core';

const dcupl = new Dcupl(); // options: /sdk/api/dcupl#dcuplinitoptions

dcupl.models.set({
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'brand', type: 'string' },
    { key: 'price', type: 'int' },
  ],
});

dcupl.data.set(
  [
    { key: 'p1', name: 'Laptop', brand: 'Acme', price: 999 },
    { key: 'p2', name: 'Mouse', brand: 'Acme', price: 29 },
    { key: 'p3', name: 'Desk', brand: 'Oak & Co', price: 299 },
  ],
  { model: 'Product' },
);

// Builds the indexes. Resolves false, and logs the error, if processing fails.
await dcupl.init();

Load with the loader

Instead of models.set() and data.set(), load a dcupl.lc.json and the files it lists:

setup.ts
import { Dcupl } from '@dcupl/core';
import { DcuplAppLoader } from '@dcupl/loader';

const dcupl = new Dcupl();
const loader = new DcuplAppLoader();
dcupl.loaders.add(loader);

// The request url is `${baseUrl}/${loaderFileName}`.
// baseUrl must be non-empty; an empty string falls back to the dcupl CDN default.
await loader.config.fetch({
  baseUrl: window.location.origin, // browser: your app's origin; in Node, pass a full URL
  loaderFileName: 'dcupl.lc.json',
});

await loader.process(); // loads every resource the configuration lists
await dcupl.init();

Lists and queries

query.ts
const list = dcupl.lists.create({ modelKey: 'Product' });

// Conditions on different attributes combine with AND, on the same attribute with OR
list.catalog.query.addCondition({ attribute: 'brand', operator: 'eq', value: 'Acme' });
list.catalog.query.addCondition({ attribute: 'price', operator: 'gte', value: 50 });
list.catalog.query.items(); // → Laptop

// A group with an explicit groupType ('or' or 'and')
list.catalog.query.clear();
list.catalog.query.addGroup({
  groupKey: 'cheap-or-oak',
  groupType: 'or',
  queries: [
    { attribute: 'brand', operator: 'eq', value: 'Oak & Co' },
    { attribute: 'price', operator: 'lt', value: 50 },
  ],
});
list.catalog.query.items(); // → Desk, Mouse
list.catalog.query.removeGroup('cheap-or-oak');

// Sort and paginate (order is 'ASC' or 'DESC')
list.catalog.query.applyOptions({ sort: { attributes: ['price'], order: ['DESC'] }, start: 0, count: 2 });
list.catalog.query.items(); // → Laptop, Desk
list.catalog.query.first(); // → Laptop
list.catalog.query.count(); // items on the current page → 2

// Remove all conditions, sort and pagination
list.catalog.query.clear();

Operators: Query Operators. Every query method: Catalog API.

Analysis functions

fn.ts
// Counts per value, for filter UIs
list.catalog.fn.facets({ attribute: 'brand' });
// → [{ value: 'Acme', size: 2, ... }, { value: 'Oak & Co', size: 1, ... }]

// sum, avg, count, min, max, distinct
list.catalog.fn.aggregate({ attribute: 'price', types: ['min', 'max', 'avg'] });
// → { attribute: 'price', min: 29, max: 999, avg: 442.3333333333333, ... }

// Items grouped by a value
list.catalog.fn.groupBy({ attribute: 'brand' });
// → { items: [{ value: 'Acme', _meta: { size: 2 }, ... }, { value: 'Oak & Co', _meta: { size: 1 }, ... }], ... }

// Autocomplete ('/…/' is a regex)
list.catalog.fn.suggest({ attribute: 'name', value: '/^la/', regexFlags: 'i' });
// → [{ value: 'Laptop', keys: ['p1'] }]

// All items matching the current query (count() is capped by the page size)
list.catalog.fn.metadata().currentSize; // → 3

Options and return types: Functions API.

Global queries

Read a model without creating a list:

global-query.ts
dcupl.query.one({ modelKey: 'Product', itemKey: 'p1' });
// → { key: 'p1', name: 'Laptop', brand: 'Acme', price: 999 }

dcupl.query.execute({
  modelKey: 'Product',
  queries: [
    { attribute: 'brand', operator: 'eq', value: 'Acme' },
    { attribute: 'price', operator: 'gte', value: 50 },
  ],
  sort: { attributes: ['price'], order: ['ASC'] },
  count: 20,
});
// → [{ key: 'p1', name: 'Laptop', ... }]

Update data

Data changes are staged until you call dcupl.update(); lists then return the new results.

update.ts
// Change existing items; properties you leave out keep their values
dcupl.data.update([{ key: 'p1', price: 899 }], { model: 'Product' });

// Add new items, or update them if they exist
dcupl.data.upsert([{ key: 'p4', name: 'Lamp', brand: 'Oak & Co', price: 40 }], { model: 'Product' });

// Remove items by key
dcupl.data.remove([{ key: 'p2' }], { model: 'Product' });

// Apply the staged changes. Resolves false, and logs the error, if the update fails.
await dcupl.update();

list.catalog.query.items(); // → Laptop (899), Desk, Lamp

Events

events.ts
// Every event of the instance: dcupl_initialized, dcupl_updated_manually, list_created, ...
const off = dcupl.on((message) => console.log(message.type));

// Re-render when a list's result changes, e.g. after dcupl.update()
const offList = list.onResultChange(() => render(list.catalog.query.items()));

// Unsubscribe
off();
offList();

Clean up

cleanup.ts
list.destroy(); // a list you no longer need
dcupl.destroy(); // the whole instance, e.g. when your app unmounts

See also