LioranDB TypeScript Series #4: Type-Safe CRUD with Databases and Collections
LioranDB TypeScript Series: Build with a developer-first document database powered by Rust and designed for TypeScript.
We have a connection.
Now let’s store actual application data.
Define your model
type Product = {
_id?: string;
sku: string;
title: string;
price: number;
inStock: boolean;
category: string;
};
Enter fullscreen mode Exit fullscreen mode
Then create a typed collection:
const db = client.db("default");
const products =
db.collection<Product>("products");
Enter fullscreen mode Exit fullscreen mode
Now your application gets TypeScript assistance when working with documents.
Insert one document
const result = await products.insertOne({
sku: "bk-001",
title: "Blue Notebook",
price: 149,
inStock: true,
category: "stationery",
});
console.log(result.insertedId);
Enter fullscreen mode Exit fullscreen mode
Insert multiple documents
await products.insertMany([
{
sku: "bk-002",
title: "Black Notebook",
price: 199,
inStock: true,
category: "stationery",
},
{
sku: "pn-001",
title: "Marker",
price: 49,
inStock: true,
category: "writing",
},
]);
Enter fullscreen mode Exit fullscreen mode
Find documents
const available = await products
.find({ inStock: true })
.sort({ price: 1 })
.limit(10)
.toArray();
Enter fullscreen mode Exit fullscreen mode
Find one:
const product = await products.findOne({
sku: "bk-001",
});
Enter fullscreen mode Exit fullscreen mode
Count documents:
const total = await products.countDocuments();
const availableCount =
await products.countDocuments({
inStock: true,
});
Enter fullscreen mode Exit fullscreen mode
Update documents
await products.updateOne(
{ sku: "bk-001" },
{
$set: {
inStock: false,
},
}
);
Enter fullscreen mode Exit fullscreen mode
Or update multiple:
await products.updateMany(
{ category: "stationery" },
{
$set: {
inStock: true,
},
}
);
Enter fullscreen mode Exit fullscreen mode
Delete documents
await products.deleteOne({
sku: "bk-001",
});
Enter fullscreen mode Exit fullscreen mode
Or:
await products.deleteMany({
category: "discontinued",
});
Enter fullscreen mode Exit fullscreen mode
Query operators
Filters can go beyond equality.
const productsOver100 = await products
.find({
price: {
$gte: 100,
},
})
.toArray();
Enter fullscreen mode Exit fullscreen mode
Text queries are also available when the corresponding text index exists:
const matches = await products.find({
$text: {
$search: "Notebook",
$field: "title",
},
}).toArray();
Enter fullscreen mode Exit fullscreen mode
We’ll explore that properly in the next two articles.
Idempotency keys
For write operations where retry safety matters, you can provide an idempotency key:
await products.insertOne(
{
sku: "bk-100",
title: "Engineering Notebook",
price: 299,
inStock: true,
category: "stationery",
},
{
idempotencyKey: "product-bk-100-create",
}
);
Enter fullscreen mode Exit fullscreen mode
This is especially useful around network retries and application workflows where accidentally performing the same logical write twice would be painful.
Database operations
The database handle itself supports operations such as:
db.collection("products");
await db.createCollection("events");
await db.listCollections();
await db.dropCollection("old-data");
Enter fullscreen mode Exit fullscreen mode
And when you really mean it:
await db.dropDatabase();
Enter fullscreen mode Exit fullscreen mode
Maybe don’t put that last one behind a giant red button called “probably fine”. 😭
Resources
Database & Collections Docs:
https://docs.liorandb.com/docs/driver/database-and-collections
Documentation: https://docs.liorandb.com
Website: https://liorandb.com
Creator: https://github.com/UltronTheAI
Previous: Part 3 → Connections & Lifecycle
Next: Part 5 → FindCursor, Query Shaping & Text Search