Getting Started
This guide walks you from an empty directory to a running Calimero service written in TypeScript.
Prerequisites
Section titled “Prerequisites”- Node.js >= 18
- pnpm >= 8 (npm works too)
- A running Calimero node (
merod) and themeroctlCLI to install and call your service - Linux or macOS — the build toolchain downloads a WASI SDK and QuickJS compiler that are not published for Windows (use WSL)
Install
Section titled “Install”The SDK is split into two packages: the runtime library and the build CLI.
npm install @calimero-network/calimero-sdk-jsnpm install -D @calimero-network/calimero-cli-js# or with pnpmpnpm add @calimero-network/calimero-sdk-jspnpm add -D @calimero-network/calimero-cli-jsWrite your first service
Section titled “Write your first service”A Calimero service has two halves:
- a state class marked
@State— the data that is persisted and synced, and - a logic class marked
@Logic(StateClass)— the methods clients call.
Create src/index.ts:
import { State, Logic, Init, View, createCounter } from '@calimero-network/calimero-sdk-js';import type { Counter } from '@calimero-network/calimero-sdk-js/collections';import * as env from '@calimero-network/calimero-sdk-js/env';
@Stateexport class CounterApp { // Initialize CRDT fields inline with the factory helpers so the runtime // reuses the persisted collection ID on every invocation. count: Counter = createCounter();}
@Logic(CounterApp)export class CounterLogic extends CounterApp { @Init static initialize(): CounterApp { env.log('Initializing counter'); return new CounterApp(); }
increment(): void { this.count.increment(); env.log('Counter incremented'); }
@View() getCount(): bigint { return this.count.value(); }}Key points:
- Initialize CRDT fields inline with the
create*helpers (createCounter(),createUnorderedMap(), …). The logic class methods run withthisbound to a hydrated state instance, so inline defaults guarantee the runtime reattaches the persisted collection ID instead of allocating a fresh one. - Mark read-only entry points with
@View(). The dispatcher then skips the persistence pipeline for that method — no state snapshot, no delta, no gossip. A method without@View()is assumed to mutate state.
Build to WASM
Section titled “Build to WASM”The calimero-sdk build command compiles your TypeScript through Rollup →
QuickJS → WASI-SDK into a single WASM module, emitting an ABI manifest alongside
it:
npx calimero-sdk build src/index.ts -o build/service.wasmThis produces build/service.wasm plus build/abi.json (the ABI manifest used
for Rust-compatible serialization and for client generation).
Useful flags:
-o, --output <path>— output path (defaultbuild/service.wasm).--no-optimize— skip thewasm-optpass for faster dev builds. Optimization is on by default.--verbose— show every pipeline stage (Rollup, QuickJS, Clang, optimization).
There is also calimero-sdk validate src/index.ts to check a source file before building.
Deploy and call
Section titled “Deploy and call”Install the built WASM on a node and call its methods with meroctl:
# Install the service into a contextmeroctl --node node1 app install --path build/service.wasm
# Call a mutating methodmeroctl --node node1 call --context-id <CONTEXT_ID> --method increment
# Call a viewmeroctl --node node1 call --context-id <CONTEXT_ID> --method getCountNode-local private storage
Section titled “Node-local private storage”For data that should stay on the executing node and not replicate through CRDT deltas (cached secrets, per-node bookkeeping), use the private storage helper:
import { createPrivateEntry } from '@calimero-network/calimero-sdk-js';
const secrets = createPrivateEntry<{ token: string }>('private:secrets');
// Read-or-initializeconst current = secrets.getOrInit(() => ({ token: '' }));
// Mutate in place (initialiser is used when no value exists yet)secrets.modify( value => { value.token = 'rotated-token'; }, () => ({ token: '' }));PrivateEntryHandle also exposes get(), set(value), remove(), and
getOrDefault(value). Entries are node-local — they never appear in CRDT deltas
or gossip. See the Private data guide for the full API
and when to use it.
Next steps
Section titled “Next steps”- CRDT Collections — the data types that sync automatically.
- Events — emit and handle events across nodes.
- Client Generation — generate a typed client from your ABI.
- Architecture — how the build and runtime work.
- API Reference — decorators,
envfunctions, and collection methods.