.md

Core concepts

Before diving into code, let's understand the key concepts that make dcupl work. This 5-minute overview will give you the mental model you need to be productive.

What is the dcupl core SDK?

The SDK is an in-memory query engine that runs in your browser or Node.js environment, a mini-database for your application: data loads once, and filtering, sorting and facets run locally without a network round trip. Why that matters and where it does not fit is on the Introduction.

The three core concepts

Concept What it is
Model A schema defining your data structure (properties + references)
List A queryable view of items from a model
Query The interface for filtering, sorting, and analyzing data
flowchart LR
  M[Model] --> L[List]
  L --> Q[Query]
  Q --> R[Results]

1. Models

Models define the structure of your data - like database tables or TypeScript interfaces. They include properties (fields) and references (relationships).

import type { ModelDefinition } from '@dcupl/common';

const productModel: ModelDefinition = {
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'float' },
    { key: 'category', type: 'string' },
  ],
  references: [{ key: 'brand', type: 'singleValued', model: 'Brand' }],
};

Think of it as: The blueprint for your data

Learn more: Models

2. Lists

Lists are filtered views of your data. You create a list to query a specific model.

const productList = dcupl.lists.create({ modelKey: 'Product' });

Think of it as: A live view of your data that updates when filters change

Learn more: Lists

3. Query

Query is the interface attached to each list for filtering, sorting, and retrieving data.

productList.catalog.query.addCondition({ attribute: 'price', operator: 'gte', value: 100 }); // Filter
productList.catalog.query.items(); // Get results
productList.catalog.fn.facets({ attribute: 'category' }); // Get facets

Think of it as: The SQL-like query language for your data

Learn more: Query

Architecture overview

The dcupl SDK consists of modular packages that work together:

flowchart TB
  subgraph Packages["SDK Packages"]
    direction TB
    CORE["@dcupl/core<br/><small>Query engine, models, lists</small>"]
    LOADER["@dcupl/loader<br/><small>Config & data loading</small>"]
    COMMON["@dcupl/common<br/><small>Shared types & utilities</small>"]
  end

  subgraph DataSources["Data Sources"]
    direction TB
    CONSOLE["dcupl Console<br/><small>Managed backend</small>"]
    LOCAL["Local Files<br/><small>JSON, CSV</small>"]
    API["Custom APIs<br/><small>JSON or CSV over HTTP</small>"]
  end

  subgraph Runtime["Runtime"]
    direction TB
    MODELS["Models & Data"]
    LISTS["Lists"]
    QUERY["Query API"]
    RESULTS["Query results"]
  end

  DataSources --> LOADER
  LOADER --> CORE
  COMMON -.-> CORE
  COMMON -.-> LOADER
  CORE --> MODELS
  MODELS --> LISTS
  LISTS --> QUERY
  QUERY --> RESULTS

Package responsibilities

Package Purpose
@dcupl/core The query engine - models, lists, query API
@dcupl/loader Configuration fetching, resource loading, transformers
@dcupl/common Shared TypeScript types and utilities

The data flow

Here's how data moves through the system:

flowchart LR
  subgraph Setup["1. Setup"]
    direction TB
    DEF["Define Models"]
    LOAD["Load Data"]
  end

  subgraph Init["2. Initialize"]
    INIT["dcupl.init()"]
  end

  subgraph Query["3. Query"]
    direction TB
    LIST["Create List"]
    FILTER["Add Filters"]
    EXEC["Execute Query"]
  end

  subgraph Output["4. Results"]
    direction TB
    ITEMS["Items"]
    FACETS["Facets"]
    AGGS["Aggregations"]
  end

  Setup --> Init --> Query --> Output

Step by step:

  1. Define Models - Create schemas for your data
  2. Load Data - Add data inline or via DcuplAppLoader
  3. Initialize - Call dcupl.init() to process models and data
  4. Create Lists - Create filtered views of your models
  5. Query - Use query API to filter, sort, analyze
  6. Get Results - Items, facets, aggregations, pivot tables

Simple example

Let's see all three concepts in action:

import { Dcupl } from '@dcupl/core';

// Create instance
const dcupl = new Dcupl();

// 1. Define MODEL with properties
dcupl.models.set({
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'float' },
  ],
  data: [
    { key: 'p1', name: 'Laptop', price: 999 },
    { key: 'p2', name: 'Mouse', price: 29 },
  ],
});

// Initialize
await dcupl.init();

// 2. Create LIST
const list = dcupl.lists.create({ modelKey: 'Product' });

// 3. QUERY via catalog
list.catalog.query.addCondition({
  attribute: 'price',
  operator: 'gte',
  value: 100,
});

const results = list.catalog.query.items();
// Results: [{ key: 'p1', name: 'Laptop', price: 999 }]

Key takeaways

  1. Models define structure (properties + references)
  2. Lists are filtered views of data
  3. Query is how you filter and analyze data
  4. You can have multiple lists for the same model
  5. Everything starts with dcupl.init() and ends with list.catalog.query