The Elegance SDK is an SDK for quickly building real-time AI, full-stack JavaScript applications using SingleStoreDB with support for MySQL and SingleStore Kai™ (support for applications built on MongoDB®) connection types and the OpenAI API.
It provides a set of ready-made tools for implementing various types of functionality when you need to transact, analyze and contextualize data in real time — or build real-time AI apps.
The Elegance SDK includes a list of features like vector search, chat completions, file embeddings generation (CSV, PDF), MySQL and MongoDB aggregate queries, MySQL and MongoDB database connections support, Node.js controllers and React.js hooks.
There are two options for using the SDK: add the SDK to an existing project, or use an npx command to install Next.js or Express.js template.
Start with a template
To quickly start using the Elegance SDK, you can install Next.js or Express.js template using the following command:
npx create-singlestoredb-app --template elegance-next|express
Next, you’ll need to set the environment variables by updating the values in the .env file.
Now you can start the development process by running:
npm run dev
Add the SDK to an existing project
To use the SDK in an existing project, you need to install the @singlestore/elegance-sdk npm package on your JavaScript server and client by running the following command:
npm install @singlestore/elegance-sdk
Server setup
To use the SDK on a server, you only need to create a .env and an eleganceServerClient files, and create a server client with the following code:
MySQL connection type:
# ./.env
DB_HOST=svc-*.singlestore.com
DB_USER=user
DB_PASSWORD=passowrd
DB_NAME=database_name
OPENAI_API_KEY=sk-*
// ./services/eleganceServerClient.ts
import { createEleganceServerClient } from "@singlestore/elegance-sdk/server";
export const eleganceServerClient = createEleganceServerClient("mysql", {
connection: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
},
ai: {
openai: {
apiKey: process.env.OPENAI_API_KEY
}
}
});
SingleStore Kai™ (API for apps built on MongoDB®) connection type:
# ./.env
KAI_URI=mongodb://*.singlestore.com
DB_NAME=database_name
OPENAI_API_KEY=sk-*
// ./services/eleganceServerClient.ts
import { createEleganceServerClient } from "@singlestore/elegance-sdk/server";
export const eleganceServerClient = createEleganceServerClient("kai", {
connection: {
uri: process.env.KAI_URI,
database: process.env.DB_NAME
},
ai: {
openai: {
apiKey: process.env.OPENAI_API_KEY
}
}
});
First, you need to specify the connection type — the Elegance SDK supports MySQL and Kai connection types. Then you need to provide your database credentials; and if you want to use AI-related features, you need to provide an OpenAI API key.
In case if you don't want to use OpenAI, you can replace the existing AI SDK logic with customizers:
// ./services/eleganceServerClient.ts
...
import {
ChatCompletionBody,
EmbeddingInput
} from "@singlestore/elegance-sdk/types";
...
ai: {
cusromizers: {
createEmbedding: async (input: EmbeddingInput) => {
const embedding = await customFn(input);
return embedding;
},
createChatCompletion: async (body: CreateChatCompletionBody) => {
const chatCompletion = await customFn(body);
return chatCompletion;
}
}
}
...
Once the eleganceServerClient is created, you need to create a route handler to handle requests from the client.
Express.js:
# ./.env
...
PORT=4000
...
// ./index.ts
import express from "express";
import cors from "cors";
import type { Routes } from "@singlestore/elegance-sdk/server";
import { eleganceServerClient } from "@/services/eleganceServerClient";
import "dotenv/config";
const PORT = process.env.SERVER_PORT;
const app = express();
app.use(cors({ origin: ["http://localhost:3000"]}));
app.use(express.json());
const apiRouter = express.Router();
apiRouter.post("/elegance/:route", async (req, res) => {
try {
const route = req.params.route as Routes;
const result = await eleganceServerClient.handleRoute(route, req.body);
return res.status(200).json(result);
} catch (error: any) {
return res.status(error.status).json(error);
}
});
app.use("/api", apiRouter);
app.listen(PORT, () => {
console.log(Server started on port: ${PORT}
);
});
Next.js:
// ./api/elegance/[route]/route.ts
import { NextResponse } from "next/server";
import { Routes } from "@singlestore/elegance-sdk/server";
import { eleganceServerClient } from "@/services/eleganceServerClient";
export async function POST(request: Request, { params }: { params: { route: Routes } }) {
try {
const body = await request.json();
const result = await eleganceServerClient.handleRoute(params.route, body);
return NextResponse.json(result);
} catch (error: any) {
return NextResponse.json(error, { status: error.status });
}
}
Now, the server is ready.
Client setup
To use the SDK on a client, you only need to create an eleganceClient file and create a client with the following code:
# ./.env
...
SERVER_URL=http://localhost:4000
...
// ./services/eleganceClient.ts
import { createEleganceClient } from "@singlestore/elegance-sdk";
const baseURL = `${process.env.SERVER_URL}/api`;
export const eleganceClient = createEleganceClient("mysql", { baseURL });
Here, baseURL is the server address and API entry point.
Usage
Now that all the parts are configured, we are ready to use the SDK. The eleganceClient provides ready-to-use React.js hooks and requests.
React.js hooks execute requests and have ready-made state handlers that handle value, loading and error states. Each hook provides helper functions for manually setting values, errors and loading states. You can also provide a hook with configuration that takes an initial value, initial loading state and an error handler.
Requests are standard JavaScript fetch requests that can be used throughout your code. You can use it in client and server components or utilities. Think of them like a regular Promise object.
The eleganceClient includes the following operations:
- ChatCompletion. Creates a chat completion based on the prompt
- CreateAndInsertFileEmbeddings. Accepts a CSV or PDF file, creates embeddings and inserts them into a new collection
- CreateEmbedding. Creates an embeddings based on the provided value
- CreateFileEmbeddings. Accepts a CSV or PDF file, and returns embeddings
- DeleteMany. Deletes many records based on condition
- FindMany. Finds and returns records based on condition
- FindOne. Finds a record based on condition
- InsertMany. Inserts many records into a collection
- InsertOne. Inserts one record into a collection
- Query. Executes MySQL or aggregate query
- UpdateMany. Updates many records with a new value based on condition
- VectorSeach. Executes vector search based on prompt
Let's assume that your database has a collection of article embeddings and you want to find the text corresponding to the prompt to create a chat completion based on the found text.
For that, let’s create a search function:
import { eleganceClient } from "@/services/eleganceClient";
const createArticleChatCompletion = (prompt: string) => {
return eleganceClient.requests.chatCompletion({
prompt,
table: "articles",
embeddingField: "embedding",
textField: "text"
});
};
You can now use this function to get a chat completion based on your prompt anywhere in code. If you’re using React.js, you would write the code like this:
// ./src/components/ArticleChatCompletion.tsx
import { eleganceClient } from "@/services/eleganceClient";
const ArticleChatCompletion = ({ prompt }: { prompt: string }) => {
const articleChatCompletion = eleganceClient.hooks.useChatCompletion();
const { execute: getArticleChatCompletion } = articleChatCompletion;
useEffect(() => {
if (prompt) {
getArticleChatCompletion({
prompt,
table: "articles",
embeddingField: "embedding",
textField: "text"
});
}
}, [prompt, getArticleChatCompletion]);
if (articleChatCompletion.error) {
return <p>{articleChatCompletion.error}</p>;
}
return <p>{articleChatCompletion.value}</p>;
};
Or let's say you want to run a MySQL query. You would write code similar to the following:
// ./src/components/Step.tsx
const Step = () => {
const query = eleganceClient.hooks.useQuery();
const { execute: executeQuery } = query;
const handleStepChange = (stepId: number) => {
executeQuery({ query: `SELECT id, displayName FROM steps WHERE id = ${stepId}` });
};
if (query.error) {
return <p>{query.error}</p>;
}
return (
<div>
<p>Current step: {query.value.displayName}</p>
<button onClick={() => handleStepChange(query.value.id + 1)}>Next step</button>
</div>
);
};
This was a small example of how you can use the Elegance SDK — and you can build much more complex logic and functionality.
Try SingleStore Elegance today.