Build your first dcupl application
In this tutorial, you will build a complete dcupl application from scratch. By the end, you will have a working app that loads data, queries it, and displays results.
Time required: 15 minutes
What you will build: A simple book library app that filters books by genre and displays results.
Prerequisites
Before starting, make sure you have:
- A supported Node.js version, see Compatibility (
node -vto check) - A code editor (VS Code recommended)
- Basic familiarity with TypeScript/JavaScript
This tutorial expands the five steps of the Quick start (create an instance, define a model, load data, initialize, query) into a small application. If you haven't run the Quick Start yet, do that first: it takes 5 minutes.
Step 1: create a new project
Create a new directory for your project, initialize it and install the dcupl core package (for yarn or pnpm, see the Quick start):
mkdir my-dcupl-app
cd my-dcupl-app
npm init -y
npm install @dcupl/coreTypeScript types are included, so no additional packages are needed.
Step 2: create the application file
Create a new file called app.ts in your project root:
import { Dcupl } from '@dcupl/core';
async function main() {
console.log('Starting dcupl app...');
// We will add code here in the next steps
}
main();Run it to make sure everything is set up correctly:
npx tsx app.tsThe first time, npx asks to download tsx (answer y), or install it in the project with npm install -D tsx. You should see "Starting dcupl app..." in your terminal.
Step 3: create a dcupl instance
The Dcupl class is the entry point for everything. Create an instance at the top of your main function:
import { Dcupl } from '@dcupl/core';
async function main() {
// Create the dcupl instance
const dcupl = new Dcupl();
console.log('dcupl instance created');
}
main();The dcupl instance is an empty container. Next, you will tell it what kind of data to expect.
Step 4: define a model
Models describe the structure of your data. Think of them like TypeScript interfaces or database schemas.
Add this model definition after creating the dcupl instance:
import { Dcupl } from '@dcupl/core';
async function main() {
const dcupl = new Dcupl();
// Define the Book model
dcupl.models.set({
key: 'Book',
properties: [
{ key: 'title', type: 'string' },
{ key: 'author', type: 'string' },
{ key: 'genre', type: 'string' },
{ key: 'year', type: 'int' },
{ key: 'rating', type: 'float' },
],
});
console.log('Model defined');
}
main();This tells dcupl that "Book" items have five properties: title, author, genre, year, and rating.
Step 5: load data
Now add some data that matches the model. In a real app, this could come from an API or file. For now, use inline data:
import { Dcupl } from '@dcupl/core';
async function main() {
const dcupl = new Dcupl();
// Define the Book model
dcupl.models.set({
key: 'Book',
properties: [
{ key: 'title', type: 'string' },
{ key: 'author', type: 'string' },
{ key: 'genre', type: 'string' },
{ key: 'year', type: 'int' },
{ key: 'rating', type: 'float' },
],
});
// Load book data
dcupl.data.set(
[
{
key: 'b1',
title: 'The Great Gatsby',
author: 'F. Scott Fitzgerald',
genre: 'Fiction',
year: 1925,
rating: 4.5,
},
{
key: 'b2',
title: 'To Kill a Mockingbird',
author: 'Harper Lee',
genre: 'Fiction',
year: 1960,
rating: 4.8,
},
{
key: 'b3',
title: 'A Brief History of Time',
author: 'Stephen Hawking',
genre: 'Science',
year: 1988,
rating: 4.6,
},
{
key: 'b4',
title: 'The Selfish Gene',
author: 'Richard Dawkins',
genre: 'Science',
year: 1976,
rating: 4.3,
},
{
key: 'b5',
title: '1984',
author: 'George Orwell',
genre: 'Fiction',
year: 1949,
rating: 4.7,
},
{
key: 'b6',
title: 'Cosmos',
author: 'Carl Sagan',
genre: 'Science',
year: 1980,
rating: 4.9,
},
],
{ model: 'Book' },
);
console.log('Data loaded');
}
main();Each item needs a unique key property. This is how dcupl identifies items internally.
Step 6: initialize dcupl
Before querying, you must call init(). It processes your models and data and makes them queryable:
// ... previous code ...
// Initialize dcupl (processes models and data)
await dcupl.init();
console.log('dcupl initialized and ready');
}
main();Run the app again to verify everything works:
npx tsx app.tsStep 7: create your first query
Now for the fun part. Create a list to query your data. A list is a queryable view of one model. It remembers the conditions you add until you call clear(): catalog.query holds filtering and results, catalog.fn holds analysis functions like facets.
Also add a Book type below the import: passed to dcupl.lists.create<Book>(), it types the items you read from the list, so your editor knows that book.title is a string.
import { Dcupl } from '@dcupl/core';
// The item type of the list's results
type Book = {
key: string;
title: string;
author: string;
genre: string;
year: number;
rating: number;
};
async function main() {
const dcupl = new Dcupl();
dcupl.models.set({
key: 'Book',
properties: [
{ key: 'title', type: 'string' },
{ key: 'author', type: 'string' },
{ key: 'genre', type: 'string' },
{ key: 'year', type: 'int' },
{ key: 'rating', type: 'float' },
],
});
dcupl.data.set(
[
{
key: 'b1',
title: 'The Great Gatsby',
author: 'F. Scott Fitzgerald',
genre: 'Fiction',
year: 1925,
rating: 4.5,
},
{
key: 'b2',
title: 'To Kill a Mockingbird',
author: 'Harper Lee',
genre: 'Fiction',
year: 1960,
rating: 4.8,
},
{
key: 'b3',
title: 'A Brief History of Time',
author: 'Stephen Hawking',
genre: 'Science',
year: 1988,
rating: 4.6,
},
{
key: 'b4',
title: 'The Selfish Gene',
author: 'Richard Dawkins',
genre: 'Science',
year: 1976,
rating: 4.3,
},
{
key: 'b5',
title: '1984',
author: 'George Orwell',
genre: 'Fiction',
year: 1949,
rating: 4.7,
},
{
key: 'b6',
title: 'Cosmos',
author: 'Carl Sagan',
genre: 'Science',
year: 1980,
rating: 4.9,
},
],
{ model: 'Book' },
);
await dcupl.init();
// Create a list for the Book model
const bookList = dcupl.lists.create<Book>({ modelKey: 'Book' });
// Get all books
const allBooks = bookList.catalog.query.items();
console.log('All books:', allBooks.length);
// Get the first book
const firstBook = bookList.catalog.query.first();
console.log('First book:', firstBook?.title);
}
main();Run it:
npx tsx app.tsYou should see:
All books: 6
First book: The Great GatsbyStep 8: add filters
Filter the list to show only science books:
// ... at the end of main(), after the Step 7 code ...
// Filter by genre
bookList.catalog.query.addCondition({
attribute: 'genre',
operator: 'eq',
value: 'Science',
});
const scienceBooks = bookList.catalog.query.items();
console.log('\nScience books:');
scienceBooks.forEach((book) => {
console.log(` - ${book.title} by ${book.author}`);
});Output:
Science books:
- A Brief History of Time by Stephen Hawking
- The Selfish Gene by Richard Dawkins
- Cosmos by Carl SaganStep 9: add multiple filters
Add another filter to find highly-rated science books:
// Add a second condition (rating >= 4.5)
bookList.catalog.query.addCondition({
attribute: 'rating',
operator: 'gte',
value: 4.5,
});
const topScienceBooks = bookList.catalog.query.items();
console.log('\nTop-rated science books (4.5+):');
topScienceBooks.forEach((book) => {
console.log(` - ${book.title} (${book.rating})`);
});Output:
Top-rated science books (4.5+):
- A Brief History of Time (4.6)
- Cosmos (4.9)Conditions on different attributes (here genre and rating) are combined with AND. Conditions on the same attribute are combined with OR, see Query logic.
Step 10: sort and paginate
Sort results and limit the output. Add the sorting code at the end of main():
// Clear previous filters
bookList.catalog.query.clear();
// Get all books sorted by year (newest first)
const sortedBooks = bookList.catalog.query.items({
sort: { attributes: ['year'], order: ['DESC'] },
});
console.log('\nBooks by year (newest first):');
sortedBooks.forEach((book) => {
console.log(` - ${book.year}: ${book.title}`);
});
// Paginate: get the first 3 books in the same order
const firstPage = bookList.catalog.query.items({
sort: { attributes: ['year'], order: ['DESC'] },
start: 0,
count: 3,
});
console.log('\nFirst page (3 books):');
firstPage.forEach((book) => {
console.log(` - ${book.title}`);
});Output:
Books by year (newest first):
- 1988: A Brief History of Time
- 1980: Cosmos
- 1976: The Selfish Gene
- 1960: To Kill a Mockingbird
- 1949: 1984
- 1925: The Great Gatsby
First page (3 books):
- A Brief History of Time
- Cosmos
- The Selfish GeneOptions passed to items() apply only to that call, so the paging call passes sort again together with start and count.
Step 11: get facet counts
Facets tell you how many items exist for each value. This is useful for building filter UIs:
// Get facet counts for genres
const genreFacets = bookList.catalog.fn.facets({ attribute: 'genre' });
console.log('\nBooks by genre:');
genreFacets.forEach((facet) => {
console.log(` - ${facet.value}: ${facet.size} books`);
});Output:
Books by genre:
- Fiction: 3 books
- Science: 3 booksComplete code
Here is the complete application, Steps 1 to 11 in one file. Its output is the output of Steps 7 to 11:
import { Dcupl } from '@dcupl/core';
// The item type of the list's results (Step 7)
type Book = {
key: string;
title: string;
author: string;
genre: string;
year: number;
rating: number;
};
async function main() {
const dcupl = new Dcupl();
dcupl.models.set({
key: 'Book',
properties: [
{ key: 'title', type: 'string' },
{ key: 'author', type: 'string' },
{ key: 'genre', type: 'string' },
{ key: 'year', type: 'int' },
{ key: 'rating', type: 'float' },
],
});
dcupl.data.set(
[
{
key: 'b1',
title: 'The Great Gatsby',
author: 'F. Scott Fitzgerald',
genre: 'Fiction',
year: 1925,
rating: 4.5,
},
{
key: 'b2',
title: 'To Kill a Mockingbird',
author: 'Harper Lee',
genre: 'Fiction',
year: 1960,
rating: 4.8,
},
{
key: 'b3',
title: 'A Brief History of Time',
author: 'Stephen Hawking',
genre: 'Science',
year: 1988,
rating: 4.6,
},
{
key: 'b4',
title: 'The Selfish Gene',
author: 'Richard Dawkins',
genre: 'Science',
year: 1976,
rating: 4.3,
},
{
key: 'b5',
title: '1984',
author: 'George Orwell',
genre: 'Fiction',
year: 1949,
rating: 4.7,
},
{
key: 'b6',
title: 'Cosmos',
author: 'Carl Sagan',
genre: 'Science',
year: 1980,
rating: 4.9,
},
],
{ model: 'Book' },
);
await dcupl.init();
// Create a list for the Book model
const bookList = dcupl.lists.create<Book>({ modelKey: 'Book' });
// Get all books
const allBooks = bookList.catalog.query.items();
console.log('All books:', allBooks.length);
// Get the first book
const firstBook = bookList.catalog.query.first();
console.log('First book:', firstBook?.title);
// Step 8: Filter by genre
bookList.catalog.query.addCondition({
attribute: 'genre',
operator: 'eq',
value: 'Science',
});
const scienceBooks = bookList.catalog.query.items();
console.log('\nScience books:');
scienceBooks.forEach((book) => {
console.log(` - ${book.title} by ${book.author}`);
});
// Step 9: Add a second condition (rating >= 4.5)
bookList.catalog.query.addCondition({
attribute: 'rating',
operator: 'gte',
value: 4.5,
});
const topScienceBooks = bookList.catalog.query.items();
console.log('\nTop-rated science books (4.5+):');
topScienceBooks.forEach((book) => {
console.log(` - ${book.title} (${book.rating})`);
});
// Step 10: Clear previous filters
bookList.catalog.query.clear();
// Get all books sorted by year (newest first)
const sortedBooks = bookList.catalog.query.items({
sort: { attributes: ['year'], order: ['DESC'] },
});
console.log('\nBooks by year (newest first):');
sortedBooks.forEach((book) => {
console.log(` - ${book.year}: ${book.title}`);
});
// Paginate: get the first 3 books in the same order
const firstPage = bookList.catalog.query.items({
sort: { attributes: ['year'], order: ['DESC'] },
start: 0,
count: 3,
});
console.log('\nFirst page (3 books):');
firstPage.forEach((book) => {
console.log(` - ${book.title}`);
});
// Step 11: Get facet counts for genres
const genreFacets = bookList.catalog.fn.facets({ attribute: 'genre' });
console.log('\nBooks by genre:');
genreFacets.forEach((facet) => {
console.log(` - ${facet.value}: ${facet.size} books`);
});
}
main();What you learned
In this tutorial, you learned how to:
- Create a dcupl instance
- Define a model with typed properties
- Load data into the model
- Initialize dcupl to process models and data
- Create a list to query data
- Filter data with conditions
- Sort and paginate results
- Get facet counts for building filter UIs
Troubleshooting
Error: "Cannot find module '@dcupl/core'"
Make sure you installed the package:
npm install @dcupl/coreError: "Model with key Book not found"
Make sure you call dcupl.models.set() before dcupl.init(), that you create the list after await dcupl.init(), and that the modelKey you pass to dcupl.lists.create() matches the model's key exactly.
Query returns no results
Check that your filter values match exactly. Filters are case-sensitive by default; to ignore case, transform both sides to lowercase:
bookList.catalog.query.addCondition({
attribute: 'genre',
operator: 'eq',
value: 'science',
options: { transform: ['lowercase'] },
});