Skip to content

Data Management

Andre Kless edited this page Mar 5, 2026 · 28 revisions

Overview

Data management in CCM is based on datastore accessors that abstract the underlying storage mechanism and provide a unified interface for reading, writing, and querying datasets.

The ccmjs framework provides two closely related API functions:

Function Result Typical Usage
ccm.store(config) Datastore accessor Continuous interaction with a datastore (read/write multiple datasets)
ccm.get(config, query) Dataset(s) One-time data retrieval

Example:

const store = await ccm.store({ name: "tasks" });
const tasks = await store.get({ done: false });

Equivalent one-time access:

const tasks = await ccm.get({ name: "tasks" }, { done: false });

A datastore accessor encapsulates the underlying storage mechanism and exposes a consistent API for interacting with datasets. Depending on the configuration, data may be stored in memory, persistently in the browser, or on a remote server. Because all datastore implementations share the same API, components remain independent of how and where data is stored.

ccm.store()

Creates a datastore accessor that provides unified access to datasets.

ccm.store() initializes a datastore based on the provided configuration and returns an accessor object exposing methods for reading, writing, and querying datasets.

Syntax

ccm.store( [config] )  Promise<Datastore>

Parameters

Parameter Type Description
config Object (optional) Datastore configuration that determines how and where datasets are stored.

Configuration Options

The behavior of ccm.store() is determined by the provided configuration. Depending on the configuration, the framework automatically selects the appropriate datastore implementation:

Condition Store Type
no name InMemoryStore
name but no url OfflineStore
name and url RemoteStore

InMemoryStore Options

Used when no name is provided.

Option Type Description
datasets Object | dataset[] Initial datasets. May be provided as { key: dataset } map or array of datasets.

OfflineStore Options

Used when name is provided but no url.

Option Type Description
name string Name of the object store inside IndexedDB.

RemoteStore Options

Used when both name and url are provided.

Option Type Description
url string Server endpoint used for datastore communication.
name string Name of the collection inside remote database.
db string Optional database identifier if the server supports multiple databases.
observe Object Query that defines which datasets should be observed.
onchange function Callback triggered when observed datasets change.
user Object User component instance used for authentication.

Return Value

Returns a Promise that resolves to a datastore accessor.

The returned accessor provides the following methods:

store.get( key_or_query )
store.set( dataset )
store.del( key )
store.count( query )
store.clear()

All methods return Promises.

Example

<!DOCTYPE html>
<html>
  <head>
    <script src="https://ccmjs.github.io/framework/ccm.js"></script>
    <script>
      (async () => {

        const store = await ccm.store({
          datasets: [
            { key: "a", value: 1 },
            { key: "b", value: 2 }
          ]
        });

        const data = await store.get("a");
        console.log(data);

      })();
    </script>
  </head>
</html>

Console output:

{ key: "a", value: 1 }

Description

ccm.store() is the primary entry point for data access in CCM.

It creates a datastore accessor that encapsulates the underlying storage mechanism and exposes a uniform API for interacting with datasets.

Depending on the configuration, CCM automatically selects one of the following datastore implementations:

  • InMemoryStore – volatile storage in a JavaScript object
  • OfflineStore – persistent storage in the browser via IndexedDB
  • RemoteStore – persistent storage on a remote server

All implementations expose the same datastore API.

Notes

  • If no configuration is provided, an InMemoryStore is created.
  • Datastore accessors are lightweight and can be created multiple times.
  • Components interact only with the datastore API and remain independent of the storage implementation.

Dataset Structure

A dataset is a plain JavaScript object containing a unique key.

Example:

{
  "key": "task1",
  "title": "Buy milk",
  "done": false
}

The key uniquely identifies a dataset within a datastore.

Apart from the key, datasets may contain arbitrary properties.
CCM does not enforce a predefined schema.

Datastore API

All datastore accessors expose the same asynchronous API for interacting with datasets.

get()

Reads one or multiple datasets.

store.get( key_or_query )

If a key is provided, the corresponding dataset is returned.

const dataset = await store.get("task1");

If a query object is provided, all matching datasets are returned.

const tasks = await store.get({ done: false });

If no dataset exists for the given key, the result is:

null

Queries used with get() follow a simple object-based matching model.

Example:

{ done: false, priority: "high" }

This query returns all datasets whose properties match the provided values.

All datastore implementations in CCM guarantee support for a portable subset of query functionality based on simple equality matching with plain JavaScript objects.

Example:

{ status: "open" }

This portable query subset works consistently across all datastore implementations:

  • InMemoryStore
  • OfflineStore
  • RemoteStore

Some datastore implementations may support more advanced query capabilities.
For example, a remote datastore backed by MongoDB may allow the full MongoDB query language.

Example (MongoDB-style query):

{ age: { $gt: 18 } }

However, such advanced queries are not guaranteed to be portable across different datastore implementations.

To ensure that components remain datastore-independent and reusable, it is recommended to rely only on the portable query subset unless a specific datastore implementation is intentionally required.

set()

Creates or updates a dataset.

store.set( dataset )

Example:

await store.set({
  key: "task1",
  title: "Buy milk"
});

If no key is provided, a key is generated automatically.

del()

Deletes a dataset.

store.del( key )

Example:

await store.del("task1");

The operation resolves to the deleted dataset or null.

count()

Counts datasets matching a query.

store.count( query )

Example:

const total = await store.count({ done: false });

clear()

Removes all datasets from the datastore.

store.clear()

Example:

await store.clear();

Storage Layers

The datastore implementation used by ccm.store() depends on the provided configuration.

CCM supports three storage layers.

InMemoryStore

Datasets are stored in a JavaScript object within the current runtime environment.

Characteristics:

  • volatile storage
  • fastest access
  • data is lost when the page reloads

Typical use cases:

  • temporary application state
  • development and testing
  • default datasets in configurations

OfflineStore

Datasets are stored persistently in the browser using IndexedDB.

Characteristics:

  • persistent across page reloads
  • entirely client-side
  • no server required

Typical use cases:

  • offline-capable applications
  • local user data
  • client-side caching

RemoteStore

Datasets are stored on a remote server.

Characteristics:

  • persistent server-side storage
  • shared across multiple clients
  • optional authentication
  • optional realtime updates via WebSocket

Typical use cases:

  • multi-user applications
  • shared application data
  • collaborative tools

ccm.get()

Reads one or more datasets from a datastore.

ccm.get() creates a datastore accessor internally and immediately performs a get() operation.

Syntax

ccm.get( [config], [key_or_query] [, projection] [, options] )  Promise<Dataset | Dataset[]>

Parameters

Parameter Type Description
config Object Datastore configuration. Same options as in ccm.store().
key_or_query string | Object Dataset key or query object.
projection Object Optional projection describing which fields should be returned.
options Object Optional query options (for example sorting or limits for remote stores).

Return Value

Returns a Promise that resolves to:

Dataset
or
Dataset[]

depending on whether a key or query was provided.

If a dataset does not exist, the result is:

null

Example

const dataset = await ccm.get(
  { name: "tasks", url: "/api" },
  "task1"
);

Example result:

{ key: "task1", title: "Buy milk" }

Query example:

const tasks = await ccm.get(
  { name: "tasks", url: "/api" },
  { done: false }
);

Description

ccm.get() is a convenience function for retrieving datasets.

Internally it performs the following steps:

ccm.store(config)
      ↓
store.get(key_or_query)

This function is especially useful when data should be read only once and no further datastore interaction is required.

It is also commonly used inside component configurations to declare data dependencies.

Notes

  • ccm.get() always creates a temporary datastore accessor internally.
  • For repeated operations on the same datastore, ccm.store() should be used instead.

Data Dependencies

CCM allows components to declare data-related dependencies directly in their configuration. These dependencies are resolved automatically during instance creation.

Two types of data dependencies exist:

  • Datastore dependencies – provide a datastore accessor
  • Dataset dependencies – load datasets directly

Datastore Dependencies

A component can declare a dependency on a datastore accessor using ccm.store().

Example:

config: {
  store: [ "ccm.store", { name: "tasks", url: "/api" } ]
}

During instance creation, CCM resolves this dependency and injects a datastore accessor into the configuration.

The component can then interact with the datastore programmatically:

const tasks = await this.store.get({ done: false });
await this.store.set({ key: "task1", done: true });

This is useful when a component needs continuous access to a datastore, for example to perform multiple read or write operations.

Dataset Dependencies

If only datasets are required, they can be loaded directly using ccm.get().

Example:

config: {
  tasks: [ "ccm.get", { name: "tasks", url: "/api" }, { done: false } ]
}

During instance creation, CCM automatically resolves this dependency and injects the retrieved datasets into the component configuration.

This allows components to declare required data declaratively, without manual loading logic.

Clone this wiki locally