Skip to content

Node.js / TypeScript SDK

Terminal window
npm install @flow-like/sdk
PackageForInstall
@lancedb/lancedbcreateLanceConnection()npm install @lancedb/lancedb
@langchain/coreLangChain wrappersnpm install @langchain/core

Choose one authentication method. The client adds /api/v1 to the base URL, so use the API origin without that suffix.

environment-client.ts
import { FlowLikeClient } from "@flow-like/sdk";
// FLOW_LIKE_BASE_URL plus FLOW_LIKE_PAT or FLOW_LIKE_API_KEY
const client = new FlowLikeClient();

For an explicit Personal Access Token:

pat-client.ts
import { FlowLikeClient } from "@flow-like/sdk";
const client = new FlowLikeClient({
baseUrl: "https://api.flow-like.com",
pat: "pat_myid.mysecret",
});

For an App API key:

api-key-client.ts
import { FlowLikeClient } from "@flow-like/sdk";
const client = new FlowLikeClient({
baseUrl: "https://api.flow-like.com",
apiKey: "flk_appid.keyid.secret",
});

Replace the sample credentials with values from your secret configuration. The Node.js client accepts pat or apiKey; the token convenience parameter is available in the Python client and the standalone LangChain wrappers.

Returns a Server-Sent Events stream:

for await (const event of client.triggerWorkflow(
"app-id",
"board-id",
"start-node-id",
{ key: "value" },
)) {
console.log(event.data);
}

Returns immediately with a run ID for polling:

const result = await client.triggerWorkflowAsync(
"app-id",
"board-id",
"start-node-id",
{ key: "value" },
);
console.log(result.run_id, result.poll_token);
// Streaming
for await (const event of client.triggerEvent("app-id", "event-id", { key: "value" })) {
console.log(event.data);
}
// Async
const result = await client.triggerEventAsync("app-id", "event-id", { key: "value" });
// Check run status
const status = await client.getRunStatus("run-id");
console.log(status.status); // "running" | "completed" | "failed"
// Poll for execution events
const poll = await client.pollExecution("poll-token", {
afterSequence: 0,
timeout: 30,
});
for (const event of poll.events) {
console.log(event);
}
// List files in an app's storage
const files = await client.listFiles("app-id", { prefix: "uploads/" });
// Upload a file
await client.uploadFile("app-id", myFile);
// Download a file
const response = await client.downloadFile("app-id", "path/to/file.pdf");
// Delete a file
await client.deleteFile("app-id", "path/to/file.pdf");
// Get resolved credentials (uri + storageOptions ready for LanceDB)
const info = await client.getDbCredentials("app-id", "_default", "read");
console.log(info.uri, info.storageOptions);
// Get raw presign response (shared_credentials enum, db_path, etc.)
const raw = await client.getDbCredentialsRaw("app-id", "_default", "write");
// List tables
const tables = await client.listTables("app-id");
// Query a table with SQL-like filter
const rows = await client.queryTable("app-id", "my-table", {
filter: "age > 25",
limit: 10,
});
// Returns a ready-to-use LanceDB Connection object
const db = await client.createLanceConnection("app-id", "write");
// Now use it with the LanceDB API
const table = await db.openTable("my-embeddings");
const results = await table.search([0.1, 0.2, 0.3]).limit(10).toArray();

The accessMode parameter controls whether you get read-only or read-write credentials:

  • "read" (default): read-only access
  • "write": full read-write access

Select a model whose api_surface is ChatCompletions. Models without that field use the same surface for compatibility. The API returns HTTP 400 if the selected model requires a different surface.

chat-completions.ts
const models = await client.listLlms();
const model = models.find(
(item) => (item.api_surface ?? "ChatCompletions") === "ChatCompletions",
);
if (!model) throw new Error("This hub has no Chat Completions model.");
const result = await client.chatCompletions(
[{ role: "user", content: "Explain quantum computing in one sentence." }],
model.bit_id,
{ temperature: 0.7, max_tokens: 200 },
);
console.log(result);
for await (const chunk of client.chatCompletionsStream(
[{ role: "user", content: "Hello!" }],
model.bit_id,
)) {
process.stdout.write(chunk.data);
}

Use responses() for a model whose api_surface is Responses. Its input and output follow the Responses schema, with input and output in place of Chat Completions messages and choices.

responses.ts
const models = await client.listLlms();
const model = models.find((item) => item.api_surface === "Responses");
if (!model) throw new Error("This hub has no Responses model.");
const result = await client.responses(
"Explain quantum computing in one sentence.",
model.bit_id,
{ max_output_tokens: 200 },
);
console.log(result.output);

For streaming, iterate client.responsesStream(input, model.bit_id) as an async iterable. Each chunk contains the event name and its JSON data string. Keep the event type when handling progress and final output.

const usage = await client.getUsage();
console.log(usage.llm_price, usage.embedding_price);

Use listEmbeddingModels() to discover available embedding model bit_id values.

const result = await client.embed("bit-id-for-embedding", [
"Hello world",
"Goodbye world",
]);
console.log(result.embeddings); // number[][]
// List all remotely-available LLMs
const llms = await client.listLlms();
for (const m of llms) {
console.log(m.bit_id, m.name, m.api_surface, m.context_length);
}
// List all remotely-available embedding models
const embeddings = await client.listEmbeddingModels();
for (const m of embeddings) {
console.log(m.bit_id, m.name, m.vector_length);
}
// Search all bits by keyword
const bits = await client.searchBits({ search: "llama", bit_types: ["Llm"] });
// Get a specific bit by ID
const bit = await client.getBit("some-bit-id");

Each model returns a ModelInfo object:

interface ModelInfo {
bit_id: string;
name: string;
description: string;
provider_name?: string;
api_surface?: "ChatCompletions" | "Responses";
model_id?: string;
context_length?: number; // LLMs only
vector_length?: number; // Embeddings only
languages?: string[];
tags: string[];
}
// List all boards in an app
const boards = await client.listBoards("app-id");
// Get a specific board
const board = await client.getBoard("app-id", "board-id");
// Create or update a board
const { id } = await client.upsertBoard("app-id", "board-id", {
name: "My Board",
description: "Processes incoming data",
});
// Delete a board
await client.deleteBoard("app-id", "board-id");
// Pre-run analysis (discover runtime variables, validate the graph)
const prerun = await client.prerunBoard("app-id", "board-id");
console.log(prerun.runtime_variables);
const apps = await client.listApps();
const app = await client.getApp("app-id");
const newApp = await client.createApp("My App", "Description");

Trigger an HTTP sink endpoint on an app:

const result = await client.triggerHttpSink("app-id", "webhook/path", "POST", {
event: "user.created",
data: { userId: "123" },
});
const health = await client.health();
console.log(health.healthy); // true

Create LangChain-compatible models from your existing client:

// Chat model
const chatModel = await client.asLangChainChat("your-model-bit-id", {
temperature: 0.7,
maxTokens: 1024,
});
// Embeddings
const embeddings = await client.asLangChainEmbeddings("your-embedding-bit-id");
import { FlowLikeChatModel, FlowLikeEmbeddings } from "@flow-like/sdk/langchain";
const chatModel = new FlowLikeChatModel({
baseUrl: "https://api.flow-like.com",
token: "pat_myid.mysecret",
bitId: "your-model-bit-id",
temperature: 0.7,
});
const embeddings = new FlowLikeEmbeddings({
baseUrl: "https://api.flow-like.com",
token: "pat_myid.mysecret",
bitId: "your-embedding-bit-id",
});
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
const chatModel = await client.asLangChainChat("your-model-bit-id");
const chain = ChatPromptTemplate
.fromMessages([
["system", "You are a helpful assistant."],
["human", "{input}"],
])
.pipe(chatModel)
.pipe(new StringOutputParser());
const response = await chain.invoke({ input: "What is Flow-Like?" });
const embeddings = await client.asLangChainEmbeddings("your-embedding-bit-id");
// Use with any LangChain vector store
const vectors = await embeddings.embedDocuments([
"Flow-Like is a visual workflow engine.",
"It runs on your device.",
]);
const queryVector = await embeddings.embedQuery("What runs locally?");
  • Node.js >= 18 (uses native fetch)
  • TypeScript >= 5.0 (for development)
  • Module system: ESM ("type": "module")

The SDK exports all relevant types:

import type {
Bit,
BitSearchQuery,
BitType,
ModelInfo,
SharedCredentials,
AwsCredentials,
AzureCredentials,
GcpCredentials,
DbPresignResponse,
LanceConnection,
} from "@flow-like/sdk";