.md

Introduction to dcupl

Your agent can write the app. It can't guess your data. dcupl gives coding agents the real model of your product data, JSON answers they can parse and a build step you approve. The same model runs in production, served via TypeScript SDK and REST API (RAPI), and via MCP where it is turned on.

Three things your agent gets that it can't make up

Agents don't crash on bad data. They succeed on the wrong data. That is the failure dcupl removes:

  • The real model, not a guess. The agent reads actual properties and types before it writes a filter (dcupl app models get --model Product). For dcupl's own formats, dcupl schemas get ModelDefinition --example does the same.
  • An answer it can parse. With --json, the app, validate, workflow, files and schemas commands answer in structured JSON, errors included. The agent knows what happened instead of scraping console text.
  • Changes you approve. Data changes are staged first. One dcupl app build applies them, when you say so.

Works with Claude Code, Cursor, Codex and Copilot. The CLI plus --json is all an agent needs; the dcupl skill for Claude Code adds command patterns and known pitfalls. See Build with agents.

What dcupl is

Technically, dcupl is a TypeScript SDK, a CLI and the Console, all working on the same models and data:

flowchart TB
  subgraph sdk["SDK (free)"]
    MODELS[Models] --> ENGINE[Query Engine]
    DATA[Data] --> ENGINE
    ENGINE --> LISTS[Lists & Catalogs]
  end

  CLI["CLI (free)"]

  subgraph console["Console (optional)"]
    EDITOR[Model Editor] --> WORKFLOWS[Data Workflows]
    WORKFLOWS --> STORAGE[CDN Storage]
    STORAGE -.->|"fetch data"| DATA
  end

  CLI -->|"dcupl app"| ENGINE
  CLI -->|"files push / pull"| STORAGE

TypeScript SDK (free)

The core @dcupl/core package runs in any JavaScript environment, in the browser or in Node.js. It provides:

  • Models - Define your data structure (like a database schema)
  • Query engine - Filter, sort, and search with a flexible query language
  • Lists and catalogs - Create queryable views of your data with facets and aggregations

The SDK is free, on npm, and works without any backend.

CLI (free)

The free @dcupl/cli is the agent's way in, and yours: dcupl init scaffolds a project, dcupl serve serves its files locally, dcupl validate checks the models, dcupl app queries the data from your terminal, and dcupl files push / dcupl files pull sync the files with the Console. With --json, the app, validate, workflow, files and schemas commands answer in JSON. See How the CLI works.

Console (optional)

The dcupl Console is a web platform that adds:

  • Visual model editor - Design data models with a UI
  • Data workflows - Load, join and clean exports (PIM dumps, ERP prices, file drops on Azure Blob or S3), run them on a dev runner first, then go to production
  • REST API deployment - Run dcupl queries server-side when needed; a deployed instance can be an MCP server where it is turned on

Files and versions, the data explorer and team roles are on the Console overview.

You can use the SDK alone for simpler projects, or add the Console for data pipelines and APIs.

Where to start

Pick the path that matches how you work:

  • You write the app in TypeScript. Start with the SDK quick start: models, data and a filtered list in a few minutes.
  • You manage data in the Console. Start with your first Console project: import a file, check the model, run the application.
  • Your agent does the work. Start with the agent setup: install the CLI, give the agent the skill and let it drive dcupl with --json.

Whichever path you take, the CLI scaffolds, serves, validates, queries and syncs the same project files.

Queries without a backend

This is the frontend-first part of dcupl. A product catalog, data table or search interface usually needs a backend round-trip for every filter change. dcupl loads the data once and runs every query locally:

flowchart LR
  subgraph traditional["Traditional approach"]
    direction TB
    FE[Frontend] -->|"filter request"| API[Backend API]
    API -->|"SQL query"| DB[(Database)]
    DB -->|"results"| API
    API -->|"JSON"| FE
  end
  subgraph dcupl["dcupl approach"]
    direction TB
    DATA[Data Source] -->|"load once"| SDK["dcupl SDK\n(in browser)"]
    SDK -->|"instant queries"| UI[Your UI]
  end

That removes three costs of the round-trip:

  • Network latency - Users wait for a network round-trip on every interaction
  • Server costs - Every user action requires server compute
  • Backend dependencies - Frontend teams wait for API endpoints

Because you load the actual data from the start (spreadsheets, CSV or JSON files, or files managed in the Console), a prototype built with dcupl reflects reality instead of breaking when the real data arrives.

When should you use dcupl?

dcupl works well for applications where users explore and filter data:

Use Case Example Why dcupl Helps
Product catalogs E-commerce, marketplaces Fast faceted search with instant filtering
Data dashboards Analytics, admin panels Client-side aggregations without server load
Search interfaces Document search, directories Instant results as users type
Configuration tools Product configurators Complex dependent filters

When to consider alternatives

dcupl is not the right choice for every project:

  • Real-time collaboration - If multiple users edit the same data simultaneously, use a real-time database like Firebase or Supabase
  • Constantly changing data - dcupl is optimized for read-heavy workloads
  • Sensitive data - Any data you ship to the browser is inspectable by the user. Keep secrets, personal data, and records users shouldn't see on the server and only send what the current user is allowed to query
  • Very large datasets in the browser - Data that is too large to download to every client doesn't belong there. Run it on a server-hosted dcupl instance exposed as a REST API, or use a dedicated search service

What dcupl is not

Not this Explanation
Not a database dcupl doesn't replace PostgreSQL or MongoDB. It loads data into memory for fast querying. You can store files on the dcupl CDN, but it is not a transactional database.
Not a CMS dcupl doesn't manage content creation or editorial workflows. Use it alongside a CMS if needed.
Not a backend framework dcupl reduces backend dependencies; it doesn't replace your entire backend.
Not a search service Search services like Algolia run server-side and charge per query, with typo tolerance and relevance ranking. dcupl queries locally and has neither.

For a detailed comparison, see dcupl vs alternatives.

Data size and limits

Measured with 1 million items: indexed queries took 25–65 ms, facets about 2.7 s. Query time grows with the number of items, and what fits depends on your models, your data and the device, because the SDK keeps all data in memory:

Environment Constraint What to do
Browser Browser memory (varies by device) Load only the data the app needs, or query a REST API instance
Node.js Node.js heap memory (default depends on system memory, often ~2–4 GB; raise it with --max-old-space-size) Increase the heap size, or run the queries on a REST API instance
Any A model holds at most about 16.7 million items (V8's Map size limit) Split the data across models or reduce it

To work with large datasets:

  • Structure data efficiently (normalize with references, avoid duplication)
  • Load only what's needed: select resources with tags, or drop rows with a parsed-file transformer in the loader
  • Run heavy queries on a REST API instance and send the results to the client, or combine both: heavy queries on the server, instant filtering of the result on the client

Where dcupl runs

Environment Use Case
Browser Interactive UIs, instant filtering, works offline once loaded
Node.js SSR, build-time data processing, scripts
REST API instance on a runner Server-side API for non-browser clients, heavy queries

Same SDK, same query API, with any JavaScript framework (React, Vue, Angular, Svelte or none). With SSR, the first page load carries real data; the browser then loads the same data into its own dcupl instance and takes over the interactions.

Who uses dcupl?

Developers who build with an agent point the agent at an export and get a typed model and a working app on real data. The agent does the keystrokes; you see what it did, what is pending and what passed, and you approve the build. See the CLI quickstart and Build with agents.

Agencies that deliver data-heavy apps for clients reuse models and workflows across projects and keep agent changes reviewable. Models, workflows and quality rules are JSON files in the repository; workflows run on a dev runner before they reach the client, and the Console canvas edits the same files the agent writes. See Workflows.

Companies that run the app and own the data get clean, typed product data for their apps and their own AI agents. The runtime can run on their own infrastructure, and a deployed REST API instance can be an MCP server where it is turned on. See REST APIs and the MCP endpoint.

Key concepts

Here are the terms you'll see throughout the docs:

Model - A definition of your data structure. Similar to a database table schema or TypeScript interface.

Property - A field in a model (name, price, category). Properties have types like string, int, float, boolean, or date.

List - A queryable view of model data. You create lists, add filter conditions, and retrieve results.

Catalog - The query interface on a list. Use it to filter, sort, get facet counts, and run aggregations.

Facets - Counts of items per value, used for filter UIs (e.g., "Electronics (24), Furniture (12)").

Reference - A property that points to items of another model, single- or multi-valued (a product's category). References let you query across models, e.g. on category.name.

init / update - Data and model changes are staged until you apply them: await dcupl.init() once, then await dcupl.update() after later changes. Both resolve to false when they fail instead of throwing, so check the result.

A simple example

Here is a complete example that loads products and filters them:

example.ts
import { Dcupl } from '@dcupl/core';

const dcupl = new Dcupl();

// 1. Define the data structure
dcupl.models.set({
  key: 'Product',
  properties: [
    { key: 'name', type: 'string' },
    { key: 'price', type: 'int' },
    { key: 'category', type: 'string' },
  ],
});

// 2. Load data (can come from an API, file, or inline)
dcupl.data.set(
  [
    { key: 'p1', name: 'Laptop', price: 999, category: 'Electronics' },
    { key: 'p2', name: 'Mouse', price: 29, category: 'Electronics' },
    { key: 'p3', name: 'Desk', price: 299, category: 'Furniture' },
    { key: 'p4', name: 'Chair', price: 199, category: 'Furniture' },
  ],
  { model: 'Product' },
);

// 3. Initialize (builds indexes for fast queries)
await dcupl.init();

// 4. Create a list and filter
const list = dcupl.lists.create({ modelKey: 'Product' });
list.catalog.query.addCondition({
  attribute: 'category',
  operator: 'eq',
  value: 'Electronics',
});

// 5. Get results
console.log(list.catalog.query.items());
// [{ key: 'p1', name: 'Laptop', ... }, { key: 'p2', name: 'Mouse', ... }]

// 6. Get facet counts for a filter UI
console.log(list.catalog.fn.facets({ attribute: 'category' }));
// [{ value: 'Electronics', size: 2, selected: true, ... },
//  { value: 'Furniture', size: 2, selected: false, ... }]

To build and run this step by step, continue with the Quick start.