Zudoku
JavaScript SDK

Resources

Every resource of the platform API has an idiomatic client on ukc: ukc.instances, ukc.volumes, ukc.services, ukc.certificates, and ukc.users. This page covers what they have in common.

Refs

Every operation on an existing resource takes a ref: { name } or { uuid }, never both.

TypeScriptCode
await ukc.instances.get({ name: "web" }); await ukc.instances.get({ uuid: "550e8400-e29b-41d4-a716-446655440000" }); // A name belongs to a metro, so a ref can carry the metro. await ukc.instances.get({ name: "web", metro: "fra" }); // A bulk operation takes one ref or an array of refs. await ukc.instances.stop([{ name: "web" }, { name: "worker" }]);

The API validates each identifier, so a name in the uuid filter fails with Invalid uuid '<name>'. The ref makes you state which kind you hold, and the SDK sends only that field.

Handles

A single-resource operation returns a handle: a lazy reference to one resource in one metro. Await the handle to get the resource. Call an operation on the handle to get another handle.

TypeScriptCode
await ukc.instances.get({ name: "web" }); // the instance await ukc.instances.get({ name: "web" }).suspend(); // the suspended instance await ukc.instances.get({ name: "web" }).update({ memory_mb: 512 }); await ukc.metro("fra").instances .create({ image: "nginx:latest" }) .wait({ state: "running", timeoutSeconds: 30 }) .logs({ offset: -4096 }); await ukc.volumes.get({ name: "data" }).attach({ attach_to: { name: "web" }, at: "/data" });

A handle sends nothing until you await it or chain onto it. Each step runs at most once, even when you await it many times.

What a chain costs

The number of requests depends on the ref and on the scope. For get(ref).suspend():

Ref and scopeRequests
{ name, metro }, any scopeOne: the suspend. The ref says where.
{ name }, one metro in scopeOne: the suspend. The scope says where.
{ name }, many metros in scopeOne concurrent read per metro to locate the resource, then the suspend in the metro that holds it. If more than one metro matches, the handle throws AmbiguousRefError.

Where a handle landed

A handle reports where it landed. A mutating step returns what the endpoint reports, so suspend() resolves to { uuid, name, state, previous_state }, not to a full instance.

TypeScriptCode
const web = ukc.instances.get({ name: "web" }); await web.where(); // "dal" await web.resolve(); // { ref: { name: "web" }, metro: "dal", baseUrl: "..." }

The direct form

Every method is also available directly, with a ref as the first argument. ukc.instances.logs({ name: "web" }, { offset: -4096 }) is shorthand for ukc.instances.get({ name: "web" }).logs({ offset: -4096 }).

Update a resource

Write a patch object, and the SDK works out the operation for each property. A value sets the property, null removes it, and an omitted or undefined property stays unchanged. This follows JSON Merge Patch.

TypeScriptCode
await ukc.instances.get({ name: "web" }).update({ memory_mb: 512, vcpus: 2 }); await ukc.instances.get({ name: "web" }).update({ env: { LOG_LEVEL: "debug" } }); await ukc.instances.get({ name: "web" }).update({ autokill: null }); await ukc.volumes.get({ name: "data" }).update({ size_mb: 2048 }); await ukc.services.get({ name: "web" }).update({ soft_limit: 5, hard_limit: 20 });

Every property has a type, so memory_mb: "512" doesn't compile. image takes the same string shorthand as create.

Merge and remove members

When a plain set isn't enough, stage the operations with edit() and send them as one request. set replaces a property, add merges into it, and del removes members of it.

TypeScriptCode
await ukc.instances.get({ name: "web" }).edit() .set({ memory_mb: 512 }) .add({ env: { LOG_LEVEL: "debug" }, tags: ["prod"] }) .del({ env: ["OLD_FLAG"], tags: ["staging"] }) .apply();

null in del() removes the whole property: ukc.services.edit({ name: "web" }).del({ domains: null }).apply().

apply() returns a handle, so the chain continues:

TypeScriptCode
await ukc.metro("fra").instances .edit({ name: "web" }) .set({ memory_mb: 1024 }) .apply() .wait({ state: "running" });

Both forms are one request. Both also take a ref instead of a handle: ukc.instances.update({ name: "web" }, { memory_mb: 512 }) and ukc.instances.edit({ name: "web" }).

The API models an update as a list of { prop, op, value } triples, and the raw triples still work: update({ name: "web" }, [{ prop: "memory_mb", op: "set", value: 512 }]).

Methods

ResourceMethods
ukc.instancescreate, get, each, list, update, edit, delete, start, stop, suspend, wait, metrics, history, logs
ukc.volumescreate, get, each, list, update, edit, delete, attach, detach
ukc.servicescreate, get, each, list, update, edit, delete
ukc.certificatescreate, get, each, list, update, delete
ukc.usersquotas, quotasByUuid
ukc.metro("fra").sandboxescreate, get, list. See Sandboxes.

A handle adds the operations of its resource:

HandleOperations
Instancerefresh, start, stop, suspend, delete, update, edit, wait, logs, metrics, history
Volumeattach, detach, update, edit, delete
Service groupupdate, edit, delete

The raw API

The idiomatic layer holds a raw client, and the raw client is one property away. The raw layer mirrors the OpenAPI specification: one method per operationId, the response envelope untouched, and one metro per call.

The SDK covers both Unikraft Cloud APIs:

  • Platform, per metro: instances, volumes, services, certificates, autoscale, and images. Idiomatic at the top level, and raw under ukc.api.platform and @unikraft/cloud/api/platform.
  • Control plane, global: the account, the metros, images, and self-hosted nodes. Raw only, under ukc.api.controlplane and @unikraft/cloud/api/controlplane.
TypeScriptCode
// From the client, with the same credentials. const res = await ukc.api.platform.instances.getInstances({ count: 10, details: true }); console.log(res.status, res.data?.instances); // From a resource. await ukc.instances.api.getInstanceLogs({ name: ["web"] }); // The control plane. const metros = await ukc.api.controlplane.metros.listMetros(); console.log(metros.data?.metros);

Everything that the idiomatic layer doesn't cover is reachable this way: autoscale, the image registry, node information, and the whole control plane.

TypeScriptCode
await ukc.api.platform.autoscale.getAutoscaleConfigurations({ uuid: ["sg1"] }); await ukc.api.platform.images.getImages({});

Standalone raw clients

A raw client also works on its own, without the idiomatic layer:

TypeScriptCode
import { PlatformApi } from "@unikraft/cloud/api/platform"; import { ControlPlaneApi } from "@unikraft/cloud/api/controlplane"; const api = new PlatformApi({ baseUrl: "https://api.fra.unikraft.cloud", token: process.env.UKC_TOKEN, }); await api.instances.getInstances({ count: 10 });

A raw client belongs to one metro. It talks to its baseUrl, and one call can go elsewhere with { baseUrl }. The fan-out across metros is the job of the idiomatic layer.

Last modified on