The SingleStore Client is a package designed for interacting with the SingleStore API in Node.js environments.
- Installation
- Example Apps
- Usage
- Initialization
- Organization
- Workspace Group
- Workspace
- Database
- Table
- Column
- Billing
- Region
- Team
- Job
- Secret
- Stage
- Storage
- Connection
npm install @singlestore/client
The SingleStoreClient can be initialized in multiple ways, depending on your needs. Below are examples of how to initialize the client in various scenarios.
Use this method if you don’t need Management API access or AI integration.
import { SingleStoreClient } from "@singlestore/client";
const client = new SingleStoreClient();
This method is used when you need to access SingleStore's management API.
import { SingleStoreClient } from "@singlestore/client";
const client = new SingleStoreClient({ apiKey: "<SINGLESTORE_API_KEY>" });
If you want to integrate AI features, use this method. You need to pass an AI instance with the required API key.
npm install @singlestore/ai
import { AI } from "@singlestore/ai";
import { SingleStoreClient } from "@singlestore/client";
const ai = new AI({ openAIApiKey: "<OPENAI_API_KEY>" });
const client = new SingleStoreClient({ ai });
- The SingleStoreClient class is flexible, allowing you to pass only the features you need (e.g., AI, API key). It will automatically configure the services based on the provided options.
- You can also use custom LLMs instead of the pre-installed OpenAI. To do this, see the
@singlestore/ai
package documentation.
Returns the current organization if an API key was provided during initialization.
const organization = await client.organization.get();
const workspaceGroups = await client.workspaceGroup.get();
const workspaceGroup = await client.workspaceGroup.get({
where: { id: "<WORKSPACE_GROUP_ID>" },
});
const workspaceGroup = await client.workspaceGroup.get({
where: { name: "<WORKSPACE_GROUP_NAME>" },
});
- To include terminated workspace groups, add the
includeTerminated: true
parameter to theworkspaceGroup.get
options object. - To select specific fields from a workspace group, add the
select: ['<FIELD_NAME_TO_SELECT>']
parameter to theworkspaceGroup.get
options object.
const { workspaceGroup, adminPassword } = await client.workspaceGroup.create({
name: "<WORKSPACE_GROUP_NAME>",
regionName: "US West 2 (Oregon)",
adminPassword: "<WORKSPACE_GROUP_PASSWORD>",
allowAllTraffic: false,
firewallRanges: ["IP_ADDRESS"],
dataBucketKMSKeyID: "<ID>",
backupBucketKMSKeyID: "<ID>",
updateWindow: { day: "mo", hour: 12 },
expiresAt: new Date("2025-01-01"),
});
- Only the
name
andregionName
fields are required to create a workspace group. All other fields are optional. - If the
adminPassword
value is not provided, a generated password is returned. - To find all available
regionName
values, refer to this link.
You can update a workspace group by specifying the workspace group ID or by calling the update
method on a selected Workspace Group instance.
await client.workspaceGroup.update("<WORKSPACE_GROUP_ID>", {
name: "<NEW_WORKSPACE_GROUP_NAME>",
adminPassword: "<NEW_WORKSPACE_GROUP_PASSWORD>",
allowAllTraffic: true,
firewallRanges: ["<NEW_IP_ADDRESS>"],
updateWindow: { day: "mo", hour: 12 },
expiresAt: new Date("2025-01-01"),
});
Updates the currently selected workspace group.
await workspaceGroup.update({
name: "<NEW_WORKSPACE_GROUP_NAME>",
adminPassword: "<NEW_WORKSPACE_GROUP_PASSWORD>",
allowAllTraffic: true,
firewallRanges: ["<NEW_IP_ADDRESS>"],
updateWindow: { day: "mo", hour: 12 },
expiresAt: new Date("2025-01-01"),
});
- All fields are optional when updating a workspace group.
You can delete a workspace group by specifying the workspace group ID or by calling the delete
method on a selected Workspace Group instance.
await client.workspaceGroup.delete("<WORKSPACE_GROUP_ID>", false);
Deletes the currently selected workspace group.
await workspaceGroup.delete(false);
- To forcibly delete a workspace group, set the optional
force
argument totrue
.
const metrics = await workspaceGroup.getMetrics();
const privateConnections = await workspaceGroup.getPrivateConnections();
const connection = client.connect({
host: "<WORKSPACE_HOST>",
user: "<WORKSPACE_USER>",
password: "<WORKSPACE_PASSWORD>",
port: <WORKSPACE_PORT>
});
const workspace = await workspaceGroup.workspace.get({
where: { id: "<WORKSPACE_ID>" },
});
if (workspace) {
const connection = workspace.connect({
user: "<WORKSPACE_USER>",
password: "<WORKSPACE_PASSWORD>",
port: <WORKSPACE_PORT>,
});
}
To connect to a free tier workspace, download the SSL certificate from this link, set the port
and database
, and place the SSL certificate in the root directory of your project.
const connection = client.connect({
host: "<WORKSPACE_HOST>",
user: "<WORKSPACE_USER>",
password: "<WORKSPACE_PASSWORD>",
port: <WORKSPACE_PORT>
database: "<DATABASE_NAME>",
ssl: { ca: readFileSync(resolve(process.cwd(), "singlestore_bundle.pem")) },
});
const workspace = await workspaceGroup.workspace.get();
const workspace = await workspaceGroup.workspace.get({
where: { id: "<WORKSPACE_ID>" },
});
const workspace = await workspaceGroup.workspace.get({
where: { name: "<WORKSPACE_NAME>" },
});
- To include terminated workspaces, add the
includeTerminated: true
parameter to theworkspaceGroup.workspace.get
options object. - To select specific fields from a workspace group, add the
select: ['<FIELD_NAME_TO_SELECT>']
parameter to theworkspaceGroup.workspace.get
options object.
const workspace = await workspaceGroup.workspace.create({
name: "WORKSPACE_NAME",
size: "S-00",
enableKai: true,
cacheConfig: 1,
scaleFactor: 1,
autoSuspend: {
suspendType: "SCHEDULED",
suspendAfterSeconds: 1200,
},
});
- Only the
name
field is required to create a workspace. All other fields are optional. - To find all available
size
values, refer to the SingleStore Helios Pricing page.
await workspaceGroup.workspace.update("<WORKSPACE_ID>", {
size: "S-00",
enableKai: true,
cacheConfig: 1,
scaleFactor: 1,
deploymentType: "PRODUCTION",
autoSuspend: {
suspendType: "SCHEDULED",
suspendAfterSeconds: 1200,
},
});
await workspace.update({
size: "S-00",
enableKai: true,
cacheConfig: 1,
scaleFactor: 1,
deploymentType: "PRODUCTION",
autoSuspend: {
suspendType: "SCHEDULED",
suspendAfterSeconds: 1200,
},
});
- All fields are optional when updating a workspace.
await workspaceGroup.workspace.delete("<WORKSPACE_ID>");
await workspace.delete();
const state = await workspaceGroup.workspace.getState("<WORKSPACE_ID>");
const state = await workspace.getState();
await workspaceGroup.workspace.resume("<WORKSPACE_ID>", { disableAutoSuspend: false });
await workspace.resume({ disableAutoSuspend: false });
- The
disableAutoSuspend
parameter is optional.
await workspaceGroup.workspace.suspend("<WORKSPACE_ID>");
await workspace.suspend();
The use
method allows you to interact with a specific database within the connection. You can optionally provide a generic DatabaseSchema
to describe the database schema and its tables.
interface DatabaseSchema extends DatabaseType {
name: "<DATABASE_NAME>";
tables: {
users: {
id: number;
};
};
}
const database = connection.database.use<DatabaseSchema>("<DATABASE_NAME>");
The create
method allows you to create a database within the connection. You can optionally provide a generic DatabaseSchema
to describe the database schema and its tables.
interface DatabaseSchema extends DatabaseType {
name: "<DATABASE_NAME>";
tables: {
users: {
id: number;
};
};
}
const database = await connection.database.create<DatabaseSchema>({
name: "<DATABASE_NAME>",
tables: {
users: {
columns: {
id: {
type: "BIGINT",
autoIncrement: true, // Optional
primaryKey: true, // Optional
nullable: false, // Optional
default: 0, // Optional
clauses: ["<CUSTOM_CLAUSE>"], // Optional
},
},
clauses: ["<CUSTOM_CLAUSE>"], // Optional
fulltextKeys: ["<COLUMN_NAME>"], // Optional
primaryKeys: ["<COLUMN_NAME>"], // Optional
},
},
});
await connection.database.drop("<DATABASE_NAME>");
await database.drop();
The query
method allows you to execute a MySQL query on the database and retrieve the result. The query result is returned as an array of rows, where each row is represented as an object with column names as keys and the corresponding values.
type RowType = { [K: string]: any }[];
const [rows] = await database.query<RowType>("<MYSQL_QUERY>");
- Ensure that the query string is properly formatted to prevent SQL errors.
- The
RowType
is a flexible type that can accommodate various column structures in the query result.
const info = await database.describe();
The showInfo
method allows you to retrieve information about the database. You can optionally request extended information by setting the isExtended
argument to true
.
const info = await database.showInfo(true);
The showTablesInfo
method allows you to retrieve information about the database tables. You can optionally request extended information by setting the isExtended
argument to true
.
const tablesInfo = await database.showTablesInfo(true);
The use
method allows you to access a specific table within the database. It optionally accepts a table name and schema, providing an interface to interact with the table for querying and manipulation.
type TableName = "<TABLE_NAME>";
type TableSchema = { [K: string]: any };
const table = database.table.use<TableName, TableSchema>("<TABLE_NAME>");
The create
method allows you to create a new table in the database. You can define the table name and schema, specifying columns and their properties such as data types, constraints, and default values.
type TableName = "<TABLE_NAME>";
type TableSchema = { id: number };
const table = await database.table.create<TableName, TableSchema>({
name: "<TABLE_NAME>",
columns: {
id: {
type: "BIGINT",
autoIncrement: true, // Optional
primaryKey: true, // Optional
nullable: false, // Optional
default: 0, // Optional
clauses: ["<CUSTOM_CLAUSE>"], // Optional
},
},
});
await database.table.drop("<TABLE_NAME>");
await table.drop();
await database.table.truncate("<TABLE_NAME>");
await table.truncate();
await database.table.rename("<TABLE_NAME>", "<TABLE_NEW_NAME>");
await table.rename("<TABLE_NEW_NAME>");
The showInfo
method allows you to retrieve information about the table. You can optionally request extended information by setting the isExtended
argument to true
.
const tableInfo = await table.showInfo(true);
The showInfo
method allows you to retrieve information about the table columns.
const tableColumnsInfo = await table.showColumnsInfo();
The insert
method allows you to insert data into a table. You can insert a single value or multiple values at once by providing an object or an array of objects that map column names to values.
await table.insert({columnName: <VALUE>})
await table.insert([{columnName: <VALUE>}, {columnName: <VALUE_2>}])
The find
method allows you to retrieve values from a table, with optional support for conditions, joins, grouping, ordering, and pagination. You can either fetch all values from a table or apply conditions to narrow down th
Retrieves all values from the table without any conditions.
const values = await table.find();
Retrieves values from the table based on the specified conditions. You can customize the query with select, join, where, groupBy, orderBy, limit, and offset options.
const values = await table.find({
select: ["<COLUMN_NAME>", "COUNT(*) AS count"], // Optional
join: [
{
type: "FULL", // Supported values: "INNER" | "LEFT" | "RIGHT" | "FULL"
table: "<JOIN_TABLE_NAME>",
as: "<JOIN_TABLE_AS>",
on: [
"<COLUMN_NAME>",
"=", // Supported values: "=" | "<" | ">" | "<=" | ">=" | "!="
"<JOIN_COLUMN_NAME>",
],
},
], // Optional
where: { columnName: "<COLUMN_VALUE>" }, // Optional
groupBy: ["<COLUMN_NAME>"], // Optional
orderBy: {
columnName: "asc", // Supported values: "asc" | "desc"
}, // Optional
limit: 10, // Optional
offset: 10, // Optional
});
- The
COUNT(*) AS count
pattern follows theclause AS alias
structure, whereCOUNT(*)
is theclause
andcount
is thealias
. - Ensure that joins, conditions, and selected columns are appropriate for the table schema and the data you're trying to retrieve.
The update
method allows you to modify existing values in the table. You provide the new values to update, along with a condition to specify which rows should be updated.
await table.update(
{ columnName: "<NEW_COLUMN_VALUE>" }, // New value
{ columnName: "<COLUMN_VALUE>" }, // Where condition
);
The delete
method allows you to remove rows from the table that match a specified condition.
await table.delete({ columnName: "<COLUMN_VALUE>" });
- Be cautious when using the
delete
method, especially if the where condition is broad, as it could result in the removal of multiple rows. - If no where condition is provided, all rows in the table will be deleted. It’s best practice to always provide a where clause to avoid accidental data loss.
The vectorSearch
method allows you to perform searches using vector-based embeddings in a specified column. This is particularly useful for tasks such as semantic search, where results are based on the similarity of vector representations of text or data.
Performs a vector search based on a prompt, returning rows from the table that match the vector similarity.
const rows = await table.vectorSearch({
prompt: "<PROMPT>",
vectorColumn: "<VECTOR_COLUMN_NAME>",
embeddingParams: {
model: "<MODEL_NAME>", // Optional
dimensions: "<VECTOR_DIMENSTION>", // Optional
}, // Optional
});
Performs a vector search with additional conditions such as selected columns, joins, filtering, grouping, ordering, and pagination.
const rows = await table.vectorSearch(
{
prompt: "<PROMPT>",
vectorColumn: "<VECTOR_COLUMN_NAME>",
embeddingParams: {
model: "<MODEL_NAME>", // Optional
dimensions: "<VECTOR_DIMENSTION>", // Optional
},
}, // Optional
{
select: ["<COLUMN_NAME>"], // Optional
join: [
{
type: "FULL", // Supported values: "INNER" | "LEFT" | "RIGHT" | "FULL"
table: "<JOIN_TABLE_NAME>",
as: "<JOIN_TABLE_AS>",
on: [
"<COLUMN_NAME>",
"=", // Supported values: "=" | "<" | ">" | "<=" | ">=" | "!="
"<JOIN_COLUMN_NAME>",
],
},
], // Optional
where: { columnName: "<COLUMN_VALUE>" }, // Optional
groupBy: ["<COLUMN_NAME>"], // Optional
orderBy: {
columnName: "asc", // Supported values: "asc" | "desc"
}, // Optional
limit: 10, // Optional
offset: 10, // Optional
}, // Optional
);
- The
vectorSearch
method returns both the table rows and av_score
field, which reflects the similarity score of each row to the search prompt. - Conditions such as
select
,join
,where
, and others can be used to refine the results further, similar to standard SQL queries.
The createChatCompletion
method allows you to generate chat completions based on a vector search within a table. Depending on the stream
option, you can retrieve the results either as a complete string or in a streamed fashion, with optional custom tools for enhancing functionality.
Performs a chat completion based on a vector search and returns the result as a complete string.
const chatCompletion = await table.createChatCompletion(
{
model: "<MODEL_NAME>", // Optional
prompt: "<PROMPT>",
systemRole: "<SYSTEM_ROLE>", // Optional
vectorColumn: "<VECTOR_COLUMN_NAME>",
stream: false,
temperature: 0, // Optional
embeddingParams: {
model: "<MODEL_NAME>", // Optional
dimensions: "<VECTOR_DIMENSTION>", // Optional
}, // Optional
},
{
select: ["<COLUMN_NAME>"], // Optional
where: { columnName: "<COLUMN_VALUE>" }, // Optional
limit: 1, // Optional
}, // Optional
);
Performs a chat completion and returns the result as a stream of data chunks.
const stream = await table.createChatCompletion(
{
stream: true,
...
},
);
const chatCompletion = await ai.chatCompletions.handleStream(stream, (chunk) => {
console.log(chunk);
});
You can also integrate custom tools to extend the functionality of the chat completion.
import { ChatCompletionTool } from "@singlestore/ai/chat-completions";
import { z } from "zod";
const customTool = new ChatCompletionTool({
name: "<TOOL_NAME>",
description: "<TOOL_DESCRIPTION>",
params: z.object({ paramName: z.string().describe("<PARAM_DESCRIPTION>") }),
call: async (params) => {
const value = await anyFnCall(params);
return { name: "<TOOL_NAME>", params, value: JSON.stringify(value) };
},
});
await table.createChatCompletion({
tools: [customTool],
...
});
- The second argument of the
createChatCompletion
method accepts the same options as the second argument of thevectorSearch
method, such asselect
,where
, andlimit
. - When using
stream: true
, thehandleStream
function is used to process the stream. It accepts a callback as the second argument, which handles each new chunk of data as it arrives.
const column = table.column.use("<COLUMN_NAME>");
const column = await table.column.add({
name: "<NEW_COLUMN_NAME>",
type: "BIGINT",
autoIncrement: false, // Optional
primaryKey: false, // Optional
nullable: true, // Optional
default: 0, // Optional
clauses: ["<CUSTOM_CLAUSE>"], // Optional
});
await table.column.modify("<COLUMN_NAME>", {
type: "BIGINT",
autoIncrement: false, // Optional
primaryKey: false, // Optional
nullable: true, // Optional
default: 0, // Optional
clauses: ["<CUSTOM_CLAUSE>"], // Optional
});
await column.modify(...)
await table.column.rename("<COLUMN_NAME>", "<NEW_COLUMN_NAME>");
await column.modify("<NEW_COLUMN_NAME>");
await table.column.drop("<COLUMN_NAME>");
await column.drop();
await table.column.showInfo("<COLUMN_NAME>");
await column.showInfo();
const billing = await client.billing.get({
metric: "ComputeCredit", // Supported values: "ComputeCredit" | "StorageAvgByte"
startTime: new Date("2024-01-01"),
endTime: new Date("2024-01-09"),
aggregateBy: "month", // Supported values: "hour", "day", "month"; Optional
});
const regions = await client.region.get();
const region = await client.region.get({ id: "<REGION_ID>" });
const region = await client.region.get({ name: "<REGION_NAME>" });
- To find all available region names, follow this link
const teams = await client.team.get();
const team = await client.team.get({ id: "<TEAM_ID>" });
const team = await client.team.get({ name: "<TEAM_NAME>" });
const team = await client.team.get({ description: "<TEAM_DESCRIPTION>" });
const team = await client.team.create({
name: "<TEAM_NAME>",
description: "<TEAM_DESCRIPTION>", // Optional
memberTeams: ["<TEAM_ID>"], // Optional
memberUsers: ["<USER_ID>"], // Optional
});
await client.team.update("<TEAM_ID>", {
name: "<NEW_TEAM_NAME>", // Optional
description: "<NEW_TEAM_DESCRIPTION>", // Optional
});
await team.update(...);
await client.team.delete("<TEAM_ID>");
await team.delete();
await client.team.addMemberTeams("<TEAM_ID>", ["<ADD_TEAM_ID>"]);
await team.addMemberTeams(["<ADD_TEAM_ID>"]);
await client.team.addMemberUsers("<TEAM_ID>", ["<ADD_USER_ID>"]);
await team.addMemberUsers(["<ADD_USER_ID>"]);
await client.team.removeMemberTeams("<TEAM_ID>", ["<REMOVE_TEAM_ID>"]);
await team.removeMemberTeams(["<REMOVE_TEAM_ID>"]);
await client.team.removeMemberUsers("<TEAM_ID>", ["<REMOVE_USER_ID>"]);
await team.removeMemberUsers(["<REMOVE_USER_ID>"]);
const job = client.job.get("<JOB_ID>");
const job = await client.job.create({
name: "<JOB_NAME>", // Optional
description: "<JOB_DESCRIPTION>", // Optional
executionConfig: {
notebookPath: "<NOTEBOOK_NAME.ipynb>",
createSnapshot: true,
runtimeName: "notebooks-cpu-small",
},
schedule: {
mode: "Recurring", // Supported values: "Once" | "Recurring"
executionIntervalInMinutes: 60,
startAt: new Date("2025-01-01"),
},
targetConfig: {
databaseName: "<DATABASE_NAME>",
resumeTarget: true,
targetID: "<TARGET_ID>",
targetType: "Workspace", // Supported values: "Workspace" | "Cluster" | "VirtualWorkspace"
}, // Optional
});
await client.job.delete("<JOB_ID>");
await job.delete();
const executions = await client.job.getExecutions("<JOB_ID>", 1, 10);
const executions = await job.getExecutions(1, 10);
const parameters = await client.job.getParameters("<JOB_ID>");
const parameters = await job.getParameters();
const runtimes = await client.job.getRuntimes();
const secrets = await client.secret.get();
const secret = await client.secret.get({ id: "<SECRET_ID>" });
const secret = await client.secret.get({ name: "<SECRET_NAME>" });
const secret = await client.secret.create({
name: "<SECRET_NAME>",
value: "<SECRET_VALUE>",
});
const secret = await client.secret.update("<SECRET_ID>", "<NEW_SECRET_VALUE>");
const secret = await secret.update("<NEW_SECRET_VALUE>");
await client.secret.delete("<SECRET_ID>");
await secret.delete();
- Folder path example:
folderName/
- File path example:
folderName/fileName.json
const stage = await workspaceGroup.stage.get();
const stage = await workspaceGroup.stage.get("<STAGE_PATH>/");
const nextStage = await stage.get("<STAGE_PATH>/");
const stage = await workspaceGroup.stage.get("<STAGE_PATH>");
const nextStage = await stage.get("<STAGE_PATH>");
await workspaceGroup.stage.update("<STAGE_PATH>", { newPath: "<NEW_STAGE_PATH>" });
await stage.update({ newPath: "<NEW_STAGE_PATH>" });
const newStage = await workspaceGroup.stage.createFolder("<NEW_STAGE_PATH>", "NEW_STAGE_NAME");
const newStage = stage.createFolder(
"<NEW_STAGE_NAME>",
"<NEW_STAGE_PATH>", // Optional
);
await workspaceGroup.stage.delete("<STAGE_PATH>");
await stage.delete();
const regions = await workspaceGroup.storage.getRegions();
const status = await workspaceGroup.storage.getStatus();
const result = await connection.client.execute("<SQL_QUERY>");