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
}