Data management
Loading, updating, and managing your data with dcupl's data API.
Data API overview
All data operations are accessed through dcupl.data:
set()- Replace all data for a modelupdate()- Incremental updates to existing itemsupsert()- Insert or update itemsremove()- Delete specific itemsreset()- Remove all items of a modelapply()- Stage a wholeDataContainer({ model, type, data },typeone of'set','update','upsert','remove'), for example one you built from a sync message
Data operations are staged. They take effect when you call dcupl.init() to initialize dcupl, or dcupl.update() to process changes after initialization.
// Data operations overview: each call is staged
dcupl.data.set([{ key: 'p1', name: 'Laptop', price: 999 }], { model: 'Product' }); // Replace all data
dcupl.data.update([{ key: 'p1', price: 899 }], { model: 'Product' }); // Update existing items
dcupl.data.upsert([{ key: 'p2', name: 'Mouse', price: 29 }], { model: 'Product' }); // Insert or update
dcupl.data.remove([{ key: 'p2' }], { model: 'Product' }); // Delete items
dcupl.data.reset({ model: 'Product' }); // Remove all items of a model
dcupl.data.apply({ model: 'Product', type: 'upsert', data: [{ key: 'p3', name: 'Desk' }] }); // A whole container
// Process the staged changes (dcupl.init() the first time)
await dcupl.update();set() - replace all data
Use when: You want to completely replace all data for a model.
This method clears existing data and replaces it with the provided items. Useful for initial data loading or full refreshes.
dcupl.data.set(
[
{ key: 'p1', name: 'Laptop', price: 999 },
{ key: 'p2', name: 'Mouse', price: 29 },
],
{ model: 'Product' }
);
await dcupl.update();update() - incremental update
Use when: You want to modify existing items. Items whose key does not exist yet are ignored, also in a model that has no data yet: update() never inserts rows. Use upsert() to add them (see update()).
Only include changed fields for better performance. Each field in the payload replaces the stored value as a whole: arrays and json objects are replaced, not merged. Fields you leave out (or set to undefined) keep their value; send null to clear a field.
dcupl.data.update(
[
{ key: 'p1', price: 899 }, // Only changed fields
{ key: 'p2', tags: ['sale'] }, // Replaces the whole tags array
],
{ model: 'Product' }
);
await dcupl.update();upsert() - insert or update
Use when: You don't know if items exist.
If an item with the key exists, it's updated. If not, it's created. Combines the behavior of insert and update.
dcupl.data.upsert(
[
{ key: 'p1', price: 899 }, // Updates if exists
{ key: 'p99', name: 'Chair' }, // Creates if doesn't exist
],
{ model: 'Product' }
);
await dcupl.update();remove() - delete items
Use when: You want to delete specific items.
Provide items with at least the key property to identify which items to remove.
dcupl.data.remove(
[{ key: 'p1' }, { key: 'p2' }],
{ model: 'Product' }
);
await dcupl.update();Data options
set(), update(), upsert() and remove() take a DataOptions object; reset() takes only { model }. An option you leave out falls back to the model's setting:
| Option | Description |
|---|---|
model |
Required. Specify which model the data belongs to |
keyProperty |
Override the primary key field (default: the model's, else 'key'). An array builds a composite key. A row with no value for it (for a composite key: no value for any of its parts) is dropped: set() and upsert() record it as a MissingKey quality error (unless autoGenerateKey gives it a key, which happens only with the default 'key'), update() and remove() skip it. An upsert() after init() that dcupl.update() applies on the partial path currently drops the row without the error; the next full rebuild (init(), or an update() that rebuilds) records it |
keyPropertySeparator |
Separator joining the parts of a composite key (default: the model's, else '::') |
autoGenerateKey |
Generate a key for rows without one. Works only with the default 'key'. When keyProperty, in these options or on the model, names another field or is an array, it is ignored and rows without a key are still dropped as MissingKey |
// Custom key property
dcupl.data.set(
[{ id: '1', name: 'Laptop' }],
{
model: 'Product',
keyProperty: 'id',
}
);
// Auto-generate keys
dcupl.data.set(
[
{ name: 'Laptop', price: 999 }, // No key provided
],
{
model: 'Product',
autoGenerateKey: true,
}
);Performance tips
Batch updates
Always batch multiple items into a single update call. Avoid calling update inside loops.
Use incremental updates
Prefer update() over set() when possible. Full replacement is slower than incremental updates.
Only include changed fields
When updating, only include the fields that actually changed. This reduces processing overhead.
// Good: Single update with multiple items
dcupl.data.update(
[
{ key: 'p1', price: 899 },
{ key: 'p2', price: 25 },
],
{ model: 'Product' }
);
await dcupl.update();
// Avoid: Multiple individual updates
const changed = [{ key: 'p1', price: 899 }, { key: 'p2', price: 25 }];
for (const product of changed) {
dcupl.data.update([product], { model: 'Product' });
await dcupl.update(); // Triggers processing each time!
}
// Good: only the changed field
dcupl.data.update([{ key: 'p1', price: 899 }], { model: 'Product' });
// Wasteful: all fields when only price changed
dcupl.data.update([{ key: 'p1', name: 'Laptop', price: 899, category: 'Electronics' }], { model: 'Product' });Best practices
Always await dcupl.update()
Data operations are queued and processed when you call dcupl.update(). Always await it before querying to ensure you get the latest data.
Missing the await can lead to stale data bugs that are hard to track down.
const list = dcupl.lists.create({ modelKey: 'Product' });
// Good: Await update before querying
dcupl.data.update([{ key: 'p1', price: 899 }], { model: 'Product' });
await dcupl.update();
const results = list.catalog.query.items();
// Bad: Missing await - data may be stale!
dcupl.data.update([{ key: 'p1', price: 799 }], { model: 'Product' });
dcupl.update(); // Not awaited!
const staleResults = list.catalog.query.items(); // Stale data!