Vix.cpp docs moved to vixcpp.com
Skip to content

First App ​

This page shows how to create your first Softadastra JavaScript SDK application.

You will build a small local-first program that opens a Softadastra client, writes a value, reads the value back, prints the result, and closes the client.

The goal is to understand the basic SDK flow before enabling persistence, sync, transport, or discovery.

What you will build ​

txt
key   : app/name
value : Softadastra SDK
size  : 1

The flow is:

txt
ClientOptions
  ↓
Client
  ↓
open
  ↓
put
  ↓
get
  ↓
close

Create a small app ​

sh
mkdir softadastra-js-first-app
cd softadastra-js-first-app

npm init -y
npm pkg set type=module
npm install @softadastra/sdk

nano main.js

Write the first app ​

Paste this code:

js
import { Client, ClientOptions } from "@softadastra/sdk";

const client = new Client(
  ClientOptions.local("node-local"),
);

const openResult = await client.open();

if (openResult.isErr()) {
  console.error(
    `failed to open client: ${openResult.error().message}`,
  );

  process.exit(1);
}

const putResult = await client.put(
  "app/name",
  "Softadastra SDK",
);

if (putResult.isErr()) {
  console.error(
    `failed to store value: ${putResult.error().message}`,
  );

  await client.close();
  process.exit(1);
}

const valueResult = await client.get("app/name");

if (valueResult.isErr()) {
  console.error(
    `failed to read value: ${valueResult.error().message}`,
  );

  await client.close();
  process.exit(1);
}

console.log("key   : app/name");
console.log(`value : ${valueResult.value().toString()}`);
console.log(`size  : ${client.size()}`);

await client.close();

Run the app ​

sh
node main.js

Expected output:

txt
key   : app/name
value : Softadastra SDK
size  : 1

Understand the code ​

The SDK import ​

js
import { Client, ClientOptions } from "@softadastra/sdk";

This gives access to the main public SDK types. For this first app, you only need Client and ClientOptions.

Create client options ​

js
const client = new Client(
  ClientOptions.local("node-local"),
);

ClientOptions.local("node-local") creates a local SDK configuration. The node id is node-local. In local mode, no WAL, transport, or discovery is configured by default.

Open the client ​

js
const openResult = await client.open();

The client must be opened before use. The SDK uses explicit result values, so you must check the result:

js
if (openResult.isErr()) {
  console.error(
    `failed to open client: ${openResult.error().message}`,
  );

  process.exit(1);
}

This is the normal SDK pattern: call operation, check result, handle error, continue only on success.

Write a value ​

js
const putResult = await client.put(
  "app/name",
  "Softadastra SDK",
);

This writes a local value. The key is app/name. The value is Softadastra SDK. This is local-first — the write does not require a server, peer, transport, discovery, or cloud access.

Always check the result:

js
if (putResult.isErr()) {
  console.error(
    `failed to store value: ${putResult.error().message}`,
  );

  await client.close();
  process.exit(1);
}

Read the value ​

js
const valueResult = await client.get("app/name");

get() reads from local state. Always check the result before reading the value:

js
if (valueResult.isErr()) {
  console.error(
    `failed to read value: ${valueResult.error().message}`,
  );

  await client.close();
  process.exit(1);
}

console.log(`value : ${valueResult.value().toString()}`);

Check the store size ​

js
console.log(`size  : ${client.size()}`);

After one successful write, the size should be 1.

Close the client ​

js
await client.close();

This gives the SDK a clean lifecycle: construct → open → use → close.

Run the existing example ​

If you are inside the JavaScript SDK repository, the same idea already exists as examples/01-local-store.js.

sh
cd ~/softadastra/sdk-js
npm install
npm run examples:local-store

Or directly:

sh
node examples/01-local-store.js

Expected output:

txt
key   : app/name
value : Softadastra SDK
size  : 1

Local repository import ​

Inside the SDK repository, examples use the local source import:

js
import { Client, ClientOptions } from "../src/index.js";

For external applications, use the package import:

js
import { Client, ClientOptions } from "@softadastra/sdk";

Result and error handling ​

This is the correct pattern:

js
const result = await client.get("app/name");

if (result.isErr()) {
  console.error(result.error().message);
  process.exit(1);
}

console.log(result.value().toString());

Avoid this pattern:

js
const value = (await client.get("app/name")).value();

That assumes the operation succeeded. In local-first systems, failures must be visible.

Why local-only first? ​

The first app uses local mode on purpose:

js
ClientOptions.local("node-local")

This proves the most important Softadastra principle: local work does not require the network.

After this works, you can add features one by one: persistent store, sync state, manual ticks, transport, discovery, metadata.

Make it persistent next ​

The first app is local and simple. To make it persistent:

js
const options = ClientOptions.persistent(
  "node-persistent",
  "data/sdk-store.wal",
);

options.autoFlush = true;

const client = new Client(options);

Create the data directory first:

sh
mkdir -p data

Inspect sync next ​

After writing a value:

js
await client.put("profile/name", "Ada");

Inspect sync state:

js
const state = await client.syncStateInfo();

if (state.isOk()) {
  console.log(`outbox : ${state.value().outboxSize}`);
  console.log(`queued : ${state.value().queuedCount}`);
  console.log(`failed : ${state.value().failedCount}`);
}

Run one sync tick:

js
const tick = await client.tick();

if (tick.isOk()) {
  console.log(`batch : ${tick.value().batchSize}`);
}

Complete app with cleanup ​

js
import { Client, ClientOptions } from "@softadastra/sdk";

const client = new Client(
  ClientOptions.local("node-local"),
);

const opened = await client.open();

if (opened.isErr()) {
  console.error(`open failed: ${opened.error().message}`);
  process.exit(1);
}

const written = await client.put("app/name", "Softadastra SDK");

if (written.isErr()) {
  console.error(`put failed: ${written.error().message}`);
  await client.close();
  process.exit(1);
}

const value = await client.get("app/name");

if (value.isErr()) {
  console.error(`get failed: ${value.error().message}`);
  await client.close();
  process.exit(1);
}

console.log(`value: ${value.value().toString()}`);

await client.close();

Common mistakes ​

Forgetting ESM mode ​

If you see Cannot use import statement outside a module:

sh
npm pkg set type=module

Forgetting to install the SDK ​

If you see Cannot find package '@softadastra/sdk':

sh
npm install @softadastra/sdk

Ignoring results ​

Wrong:

js
await client.put("key", "value");
const value = await client.get("key");
console.log(value.value().toString());

Correct:

js
const value = await client.get("key");

if (value.isErr()) {
  console.error(value.error().message);
  await client.close();
  process.exit(1);
}

console.log(value.value().toString());

Forgetting to close the client ​

After open() succeeds, close the client before exiting:

js
await client.close();
sh
mkdir softadastra-js-first-app
cd softadastra-js-first-app
npm init -y
npm pkg set type=module
npm install @softadastra/sdk
nano main.js
node main.js

Then verify the output:

txt
key   : app/name
value : Softadastra SDK
size  : 1

Summary ​

Your first JavaScript SDK app uses ClientOptions, Client, open, put, get, size, and close.

The key lesson is: Softadastra can write and read locally without a server or peer. This is the foundation of the JavaScript SDK.

Next step ​

Continue with the Client API:

Go to Client

Released under the Apache License 2.0.