Skip to content

TypeScript Examples

TypeScript examples demonstrate the gateway's services through @virtufin/api, over native gRPC in Node.

Service SDKs

Package Registry
@virtufin/api Gitea npm

@virtufin/api reaches every service through the gateway: state and pub/sub directly, WorkManager and WebSocketManager with invokeJson. It publishes under the @virtufin scope, so ordinary version ranges work:

"dependencies": {
  "@virtufin/api": "^0.8.12",
  "@connectrpc/connect-node": "^2.1.0",
  "smol-toml": "^1.3.0"
}

0.8.12 added the apiKey and transport options the cluster needs.

npm install @virtufin/api

Scoping is what makes this possible. npm resolves a private registry per scope and has no per-package mechanism, so the previous unscoped names could not be installed by name at all -- a bare npm install virtufin-api went to registry.npmjs.org, where the name is unclaimed. Consumers had to pin direct Gitea tarball URLs. If you are upgrading from that form, replace the URLs with the ranges above and rename the imports.

Authentication

Configure .npmrc (see typescript/.npmrc for the worked version):

@virtufin:registry=https://gitea.haenerconsulting.com/api/packages/virtufin/npm/
//gitea.haenerconsulting.com/api/packages/virtufin/npm/:_authToken=<token>

The host is gitea.haenerconsulting.com, not the npm.* vanity alias. Gitea advertises dist.tarball on gitea.* whatever host you query, and npm matches auth tokens by URL prefix -- so the npm.* form fetches metadata authenticated and then follows an unauthenticated redirect for the payload. Keeping both under one host lets a single token line cover them.

Connecting: the context

Each example sets CONTEXT_NAME near its top, "demo" by default: a name in ~/.config/virtufin/api/contexts.toml (see Getting Started). examples/context.ts reads the entry and builds the client:

import { experimental } from "@virtufin/naming";
import { apiClient, describe, loadContext } from "./context.js";

const CONTEXT_NAME = "demo"; // or "local" for a deploy-local backend

const context = loadContext(CONTEXT_NAME);
console.log(describe(context)); // context demo: api.virtufin.com:443 tls=true key=set
const client = apiClient(context);

apiClient uses native gRPC (createGrpcTransport from @connectrpc/connect-node) on the context's port, the gateway's gRPC port that the C# and Python clients use too, with apiKeyInterceptor for x-api-key.

Key Patterns

// State: the service must be one the gateway registers ("statestore" on demo and local).
await client.saveState("statestore", "my-key", JSON.stringify({ hello: "virtufin" }));
const got = await client.getState("statestore", "my-key");

// WorkManager and WebSocketManager through the gateway, with their JSON requests.
const call = async (service: string, method: string, request: object) =>
    JSON.parse((await client.invokeJson(service, method, JSON.stringify(request))).responseData || "{}");

const { id } = await call("websocketmanager", "Connect", {
    url: "wss://stream.binance.com:9443/ws/btcusdt@depth@100ms",
    auto_reconnect: true,
});
try {
    await call("websocketmanager", "StartPublish", { id, topic: experimental("examples", "binance-depth", "BTC_USDT") });
} finally {
    await call("websocketmanager", "Disconnect", { id }); // on demo, never leave a connection behind
}