Common Mistakes
This guide helps you identify and fix the most common mistakes beginners make with dcupl. Each section shows the wrong approach, explains why it fails, and provides the correct solution.
Forgetting to await init()
The most common mistake is querying data before dcupl has finished initializing.
The Problem
const dcupl = new Dcupl();
dcupl.models.set({ key: 'Product', properties: [...] });
dcupl.data.set([...], { model: 'Product' });
dcupl.init(); // Missing await!
const list = dcupl.lists.create({ modelKey: 'Product' });
const results = list.catalog.query.items();
// Results: [] (empty - data not ready yet)Why it fails: init() is async and builds indexes in the background. Without await, your code continues before indexes are ready.
The Fix
const dcupl = new Dcupl();
dcupl.models.set({ key: 'Product', properties: [...] });
dcupl.data.set([...], { model: 'Product' });
await dcupl.init(); // Wait for initialization
const list = dcupl.lists.create({ modelKey: 'Product' });
const results = list.catalog.query.items();
// Results: [{ key: 'p1', ... }, { key: 'p2', ... }]Tip: The same applies to
dcupl.update()after modifying data. Always await it.
Query Returns No Results
When your query returns an empty array unexpectedly, check these common causes.
Model Not Loaded
dcupl.models.set({ key: 'Product', properties: [...] });
await dcupl.init();
const list = dcupl.lists.create({ modelKey: 'Products' }); // Typo: 'Products' vs 'Product'
// Throws: Error: Model with key Products not foundDebug tip: Check registered models:
console.log('Available models:', dcupl.models.keys());Wrong Attribute Name
dcupl.models.set({
key: 'Product',
properties: [
{ key: 'productName', type: 'string' }, // Property is 'productName'
],
});
list.catalog.query.addCondition({
attribute: 'name', // Wrong: should be 'productName'
operator: 'eq',
value: 'Laptop',
});
const results = list.catalog.query.items();
// Results: [] (no match because attribute doesn't exist)Debug tip: Inspect the model definition:
const model = dcupl.models.get('Product');
console.log(
'Properties:',
model?.properties.map((p) => p.key)
);Data Type Mismatch
dcupl.data.set(
[
{ key: 'p1', price: 100 }, // price is a number
],
{ model: 'Product' }
);
list.catalog.query.addCondition({
attribute: 'price',
operator: 'eq',
value: '100', // Wrong: string instead of number
});
// Results: [] (no match - types don't match)list.catalog.query.addCondition({
attribute: 'price',
operator: 'eq',
value: 100, // Correct: number type
});Forgetting to Call items()
The query builder is fluent - it returns the query API, not results.
The Problem
list.catalog.query.addCondition({
attribute: 'category',
operator: 'eq',
value: 'Electronics',
});
const results = list.catalog.query; // Wrong: This is the query API object, not results
console.log(results.length); // undefinedThe Fix
list.catalog.query.addCondition({
attribute: 'category',
operator: 'eq',
value: 'Electronics',
});
const results = list.catalog.query.items(); // Call items() to execute
console.log(results.length); // 2Related methods that return data:
list.catalog.query.items(); // All matching items
list.catalog.query.first(); // First matching item
list.catalog.query.count(); // Number of matchesNot Cleaning Up Lists
Lists consume memory and event listeners. Forgetting to destroy them causes memory leaks.
The Problem
function showProducts(category: string) {
const list = dcupl.lists.create({ modelKey: 'Product' });
list.catalog.query.addCondition({ attribute: 'category', operator: 'eq', value: category });
return list.catalog.query.items();
// List is never destroyed - memory leak!
}
// Each call creates a new list that's never cleaned up
showProducts('Electronics');
showProducts('Furniture');
showProducts('Clothing');The Fix
Option 1: Destroy after use
function showProducts(category: string) {
const list = dcupl.lists.create({ modelKey: 'Product' });
list.catalog.query.addCondition({ attribute: 'category', operator: 'eq', value: category });
const results = list.catalog.query.items();
list.destroy(); // Clean up
return results;
}Option 2: Reuse a single list
const productList = dcupl.lists.create({ modelKey: 'Product' });
function showProducts(category: string) {
productList.catalog.query.clear(); // Reset filters
productList.catalog.query.addCondition({
attribute: 'category',
operator: 'eq',
value: category,
});
return productList.catalog.query.items();
}Option 3: Framework cleanup hooks
useEffect(() => {
const list = dcupl.lists.create({ modelKey: 'Product' });
// ... use list
return () => list.destroy(); // Cleanup on unmount
}, []);Mutating Items Directly
dcupl items are read-only by default. Direct mutations don't persist.
The Problem
const products = list.catalog.query.items();
products[0].price = 199; // Direct mutation
// The change is lost - original data unchanged
const refreshed = list.catalog.query.items();
console.log(refreshed[0].price); // Still the original priceWhy it fails: items() returns cloned data. Mutations don't affect the source.
The Fix
// Update through the data API
dcupl.data.update([{ key: 'p1', price: 199 }], { model: 'Product' });
await dcupl.update(); // Process changes
const results = list.catalog.query.items();
console.log(results[0].price); // 199 - updated!Case Sensitivity in Filters
String comparisons are case-sensitive by default.
The Problem
// Data has: { status: 'Active' }
list.catalog.query.addCondition({
attribute: 'status',
operator: 'eq',
value: 'active', // Won't match 'Active'
});
// Results: []The Fix
Option 1: Use transform for case-insensitive matching
list.catalog.query.addCondition({
attribute: 'status',
operator: 'eq',
value: 'active',
options: { transform: ['lowercase'] },
});Option 2: Match the exact case in your data
list.catalog.query.addCondition({
attribute: 'status',
operator: 'eq',
value: 'Active', // Matches 'Active' exactly
});Best practice: Normalize data during import (e.g., lowercase all status values) for consistent filtering.
Import and Installation Issues
Missing Package
import { Dcupl } from 'dcupl'; // Wrong package name
// Error: Cannot find module 'dcupl'import { Dcupl } from '@dcupl/core';Loader Not Installed
import { DcuplAppLoader } from '@dcupl/loader';
// Error: Cannot find module '@dcupl/loader'npm install @dcupl/loaderVersion Mismatch
Ensure all dcupl packages are the same version:
{
"dependencies": {
"@dcupl/core": "^1.12.0",
"@dcupl/loader": "^1.12.0"
}
}Filters Accumulating Unexpectedly
Filters persist until explicitly cleared.
The Problem
// First filter
list.catalog.query.addCondition({ attribute: 'category', operator: 'eq', value: 'Electronics' });
const electronics = list.catalog.query.items(); // Works: 2 items
// Add second filter - accumulates with first!
list.catalog.query.addCondition({ attribute: 'price', operator: 'gte', value: 500 });
const expensive = list.catalog.query.items(); // Only expensive Electronics, not all expensive itemsThe Fix
// First query
list.catalog.query.addCondition({ attribute: 'category', operator: 'eq', value: 'Electronics' });
const electronics = list.catalog.query.items();
// Clear and start fresh
list.catalog.query.clear();
list.catalog.query.addCondition({ attribute: 'price', operator: 'gte', value: 500 });
const expensive = list.catalog.query.items(); // All expensive itemsQuick Debugging Checklist
When things don't work as expected:
- Check init: Did you
await dcupl.init()? - Check model key: Is the model name spelled correctly?
- Check attribute names: Do they match your model properties?
- Check data types: Are filter values the correct type (number vs string)?
- Check case: Are string comparisons matching case?
- Check filters: Do you need to
clear()previous filters? - Check execution: Did you call
items()to get results?
// Log current state
console.log('Models:', dcupl.models.keys());
console.log('Query:', JSON.stringify(list.catalog.query.current(), null, 2));
console.log('Results:', list.catalog.query.items().length);What's Next?
- Debugging - Advanced debugging techniques
- Quick Start - Build your first query correctly
- Debugging with Console - Visual debugging tools