@datastax/astra-db-ts
astra-db-ts
is a TypeScript client for interacting with DataStax Astra DB.
This README targets v1.0.0+, which introduces a whole new API. Click here for the pre-existing client readme.
Table of contents
Quickstart
Use your preferred package manager to install @datastax/astra-db-ts
. Note that this is not supported in browsers.
Get the API endpoint and your application token for your Astra DB instance @ astra.datastax.com.
Try the following code after setting the following environment variables:
import { DataAPIClient, VectorDoc, UUID, ObjectId } from '@datastax/astra-db-ts';
interface Idea extends VectorDoc {
idea: string,
}
const client = new DataAPIClient('*TOKEN*');
const db = client.db('*ENDPOINT*', { namespace: '*NAMESPACE*' });
(async () => {
try {
const collection = await db.createCollection<Idea>('vector_5_collection', {
vector: {
dimension: 5,
metric: 'cosine'
},
checkExists: false,
});
const ideas = [
{
idea: 'An AI quilt to help you sleep forever',
$vector: [0.1, 0.15, 0.3, 0.12, 0.05],
},
{
_id: new UUID('e7f1f3a0-7e3d-11eb-9439-0242ac130002'),
idea: 'Vision Vector Frame—A deep learning display that controls your mood',
$vector: [0.1, 0.05, 0.08, 0.3, 0.6],
},
{
idea: 'A smartwatch that tells you what to eat based on your mood',
$vector: [0.2, 0.3, 0.1, 0.4, 0.15],
},
];
await collection.insertMany(ideas);
const sneakersIdea = {
_id: new ObjectId('507f191e810c19729de860ea'),
idea: 'ChatGPT-integrated sneakers that talk to you',
$vector: [0.45, 0.09, 0.01, 0.2, 0.11],
}
await collection.insertOne(sneakersIdea);
await collection.updateOne(
{ _id: sneakersIdea._id },
{ $set: { idea: 'Gemini-integrated sneakers that talk to you' } },
);
const cursor = collection.find({}, {
vector: [0.1, 0.15, 0.3, 0.12, 0.05],
includeSimilarity: true,
limit: 2,
});
for await (const doc of cursor) {
console.log(`${doc.idea}: ${doc.$similarity}`);
}
await collection.drop();
} finally {
await client.close();
}
})();
Next steps:
High-level architecture
astra-db-ts
's abstractions for working at the data and admin layers are structured as depicted by this diagram:
Here's a small admin-oriented example:
import { DataAPIClient } from '@datastax/astra-db-ts';
const client = new DataAPIClient('*TOKEN*');
const admin = client.admin();
(async () => {
const databases = await admin.listDatabases();
const dbInfo = databases[0];
console.log(dbInfo.info.name, dbInfo.id, dbInfo.info.region);
const dbAdmin = admin.dbAdmin(dbInfo.id, dbInfo.info.region);
console.log(await dbAdmin.listNamespaces());
})();
Getting the most out of the typing
astra-db-ts
is a typescript-first library, performing minimal runtime type-checking. As such, it provides
a rich set of types to help you write type-safe code.
Here are some examples of how you can properly leverage types to make your code more robust:
import { DataAPIClient, StrictFilter, StrictSort, UUID } from '@datastax/astra-db-ts';
const client = new DataAPIClient('*TOKEN*');
const db = client.db('*ENDPOINT*', { namespace: '*NAMESPACE*' });
interface Person {
_id: UUID,
name: string,
interests: {
favoriteBand?: string,
friend?: UUID,
}
}
(async () => {
const collection = await db.createCollection<Person>('my_collection', { defaultId: { type: 'uuidv7' } });
await collection.insertOne({
_id: new UUID('e7f1f3a0-7e3d-11eb-9439-0242ac130002'),
name: 'John',
interests: {
favoriteBand: 'Nightwish',
},
eyeColor: 'blue',
});
await collection.findOne({
'interests.friend': 3,
} satisfies StrictFilter<Person>, {
sort: {
name: 1,
'interests.favoriteColor': 1 as const,
} satisfies StrictSort<Person>,
});
})();
Working with Dates
Native JS Date
objects can be used anywhere in documents to represent dates and times.
Document fields stored using the { $date: number }
will also be returned as Date objects when read.
import { DataAPIClient } from '@datastax/astra-db-ts';
const client = new DataAPIClient('*TOKEN*');
const db = client.db('*ENDPOINT*', { namespace: '*NAMESPACE*' });
(async () => {
const collection = await db.createCollection('dates_test');
await collection.insertOne({ dateOfBirth: new Date(1394104654000) });
await collection.insertOne({ dateOfBirth: new Date('1863-05-28') });
await collection.updateOne(
{
dateOfBirth: new Date('1863-05-28'),
},
{
$set: { message: 'Happy Birthday!' },
$currentDate: { lastModified: true },
},
);
const found = await collection.findOne({ dateOfBirth: { $lt: new Date('1900-01-01') } });
console.log(found?.lastModified);
await collection.drop();
})();
Working with ObjectIds and UUIDs
astra-db-ts
exports an ObjectId
and UUID
class for working with these types in the database.
Note that these are custom classes, and not the ones from the bson
package. Make sure you're using the right one!
import { DataAPIClient, ObjectId, UUID } from '@datastax/astra-db-ts';
interface Person {
_id: ObjectId | UUID,
name: string,
friendId?: ObjectId | UUID,
}
const client = new DataAPIClient('*TOKEN*');
const db = client.db('*ENDPOINT*', { namespace: '*NAMESPACE*' });
(async () => {
const collection = await db.createCollection<Person>('ids_test', { defaultId: { type: 'uuidv7' } });
await collection.insertOne({ _id: new ObjectId("65fd9b52d7fabba03349d013"), name: 'John' });
await collection.insertOne({ name: 'Jane' });
const friendId = UUID.v4();
await collection.insertOne({ name: 'Alice', _id: friendId });
await collection.updateOne(
{ name: 'Jane' },
{ $set: { friendId } },
);
const jane = await collection.findOne({ name: 'Jane' });
console.log(jane?.name, jane?.friendId?.toString(), friendId.equals(jane?.friendId));
await collection.drop();
})();
Monitoring/logging
Like Mongo, astra-db-ts
doesn't provide a
traditional logging system—instead, it uses a "monitoring" system based on event emitters, which allow you to listen to
events and log them as you see fit.
Supported events include commandStarted
, commandSucceeded
, commandFailed
, and adminCommandStarted
,
adminCommandPolling
, adminCommandSucceeded
, adminCommandFailed
.
Note that it's disabled by default, and it can be enabled by passing monitorCommands: true
option to the root options'
dbOptions
and adminOptions
.
import { DataAPIClient } from '@datastax/astra-db-ts';
const client = new DataAPIClient('*TOKEN*', {
dbOptions: {
monitorCommands: true,
},
});
const db = client.db('*ENDPOINT*');
client.on('commandStarted', (event) => {
console.log(`Running command ${event.commandName}`);
});
client.on('commandSucceeded', (event) => {
console.log(`Command ${event.commandName} succeeded in ${event.duration}ms`);
});
client.on('commandFailed', (event) => {
console.error(`Command ${event.commandName} failed w/ error ${event.error}`);
});
(async () => {
const collection = await db.createCollection('my_collection', { checkExists: false });
await collection.insertOne({ name: 'Queen' });
client.removeAllListeners();
await collection.drop();
})();
Non-standard runtime support
astra-db-ts
is designed foremost to work in Node.js environments, and it's not supported in browsers. It will work
in edge runtimes and other non-node environments as well, though it'll use the native fetch
API for HTTP requests,
as opposed to fetch-h2
which provides extended HTTP/2 and HTTP/1.1 support for performance.
On Node (exactly node; not Bun or Deno with node compat on) environments, it'll use fetch-h2
by default; on others,
it'll use fetch
. You can explicitly set the fetch implementation when instantiating the client:
import { DataAPIClient } from '@datastax/astra-db-ts';
const client = new DataAPIClient('*TOKEN*', {
httpOptions: {
client: 'fetch',
},
});
Note that setting the httpOptions
implicitly sets the fetch implementation to default (fetch-h2),
so you'll need to set it explicitly if you want to use the native fetch implementation.
On some environments, such as Cloudflare Workers, you may additionally need to use the events
polyfill for the client to work properly (i.e. npm i events
). Cloudflare's node-compat won't
work here.
Check out the examples/
subdirectory for some non-standard runtime examples with more info.
HTTP/2 with minification
Due to the variety of different runtimes JS can run in, astra-db-ts
does its best to be as flexible as possible.
Unfortunately however, because we need to dynamically require the fetch-h2
module to test whether it works, the
dynamic import often breaks in minified environments, even if the runtime properly supports HTTP/2.
There is a simple workaround however, consisting of the following steps, if you truly want to use HTTP/2:
- Install
fetch-h2
as a dependency (npm i fetch-h2
) - Import the
fetch-h2
module in your code as fetchH2
(e.g. import * as fetchH2 from 'fetch-h2'
) - Set the
httpOptions.fetchH2
option to the imported module when instantiating the client
import { DataAPIClient } from '@datastax/astra-db-ts';
import * as fetchH2 from 'fetch-h2';
const client = new DataAPIClient('*TOKEN*', {
httpOptions: { fetchH2 },
});
This way, the dynamic import is avoided, and the client will work in minified environments.
Note this is not required if you don't explicitly need HTTP/2 support, as the client will default
to the native fetch implementation instead if importing fails.
(But keep in mind this defaulting will only happen if httpOptions
is not set at all).