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

Wrong: Querying before init completes
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

Correct: Always await init()
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

Wrong: Typo in model key
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 found

Debug tip: Check registered models:

console.log('Available models:', dcupl.models.keys());

Wrong Attribute Name

Wrong: Attribute name doesn't match model
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

Wrong: String vs number comparison
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)
Correct: Match the data type
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

Wrong: Expecting results from addCondition
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); // undefined

The Fix

Correct: Call items() to get results
list.catalog.query.addCondition({
  attribute: 'category',
  operator: 'eq',
  value: 'Electronics',
});

const results = list.catalog.query.items(); // Call items() to execute
console.log(results.length); // 2

Related methods that return data:

list.catalog.query.items(); // All matching items
list.catalog.query.first(); // First matching item
list.catalog.query.count(); // Number of matches

Not Cleaning Up Lists

Lists consume memory and event listeners. Forgetting to destroy them causes memory leaks.

The Problem

Wrong: Lists accumulate without cleanup
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

Correct: Destroy list when done
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

Better: 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

React: Cleanup in useEffect
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

Wrong: Mutating items directly
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 price

Why it fails: items() returns cloned data. Mutations don't affect the source.

The Fix

Correct: Use dcupl.data.update()
// 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

Wrong: Case mismatch
// 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

Correct: Use lowercase transform
list.catalog.query.addCondition({
  attribute: 'status',
  operator: 'eq',
  value: 'active',
  options: { transform: ['lowercase'] },
});

Option 2: Match the exact case in your data

Alternative: Match exact case
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

Wrong: Incorrect import
import { Dcupl } from 'dcupl'; // Wrong package name
// Error: Cannot find module 'dcupl'
Correct: Import from @dcupl/core
import { Dcupl } from '@dcupl/core';

Loader Not Installed

Wrong: Using loader without installing
import { DcuplAppLoader } from '@dcupl/loader';
// Error: Cannot find module '@dcupl/loader'
Fix: Install the loader package
npm install @dcupl/loader

Version Mismatch

Ensure all dcupl packages are the same version:

package.json
{
  "dependencies": {
    "@dcupl/core": "^1.12.0",
    "@dcupl/loader": "^1.12.0"
  }
}

Filters Accumulating Unexpectedly

Filters persist until explicitly cleared.

The Problem

Wrong: Filters accumulate
// 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 items

The Fix

Correct: Clear before new query
// 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 items

Quick Debugging Checklist

When things don't work as expected:

  1. Check init: Did you await dcupl.init()?
  2. Check model key: Is the model name spelled correctly?
  3. Check attribute names: Do they match your model properties?
  4. Check data types: Are filter values the correct type (number vs string)?
  5. Check case: Are string comparisons matching case?
  6. Check filters: Do you need to clear() previous filters?
  7. Check execution: Did you call items() to get results?
Debug helper
// 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?