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 facetsThink 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:
- Define Models - Create schemas for your data
- Load Data - Add data inline or via DcuplAppLoader
- Initialize - Call
dcupl.init()to process models and data - Create Lists - Create filtered views of your models
- Query - Use query API to filter, sort, analyze
- 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
- Models define structure (properties + references)
- Lists are filtered views of data
- Query is how you filter and analyze data
- You can have multiple lists for the same model
- Everything starts with
dcupl.init()and ends withlist.catalog.query