TeDB: TypeScript Embedded Database
TeDB (TypeScript Embedded Database) is an embedded database designed specifically for TypeScript applications, supporting various environments like Node.js, Electron, and webkit. It's currently at version 0.5.1, with a development cadence that includes regular minor fixes and improvements. A key differentiator is its pluggable storage architecture, allowing developers to implement custom storage drivers for disk persistence, in-memory operations, or even browser-based solutions like IndexedDB. Unlike some other JavaScript embedded databases, TeDB leverages an AVL balanced binary tree to index only document `_id`s and specified indexed field values in memory, preventing potential out-of-memory issues with very large datasets, as the actual document data is managed by the storage driver. All operations are Promise-based, and the library is written entirely in TypeScript, providing strong type safety.
Common errors
-
RangeError: Maximum call stack size exceeded
cause This error, especially on database load, indicates that the `binary-type-tree` index structure has become corrupted or excessively large due to a bug in older versions that caused endless appending to non-unique indices.fixUpgrade TeDB to version 0.5.0 or higher. If the problem persists with existing data, you may need to manually inspect and potentially rebuild your database indices, or restore from a backup. -
Query results are incorrect or missing when using $gt, $gte, $lt, $lte, $ne on date fields.
cause Date objects are compared lexicographically or in unexpected ways if not stored as numerical timestamps, leading to inaccurate range queries.fixEnsure all date fields in your documents are stored as `number` (e.g., `new Date().getTime()`). When querying, convert your comparison dates to timestamps as well (e.g., `{ dateField: { $gt: new Date('2023-01-01').getTime() } }`). -
Documents appear in query results after being removed, or index doesn't reflect actual data state.
cause After `remove` operations, the in-memory indices might still hold references to keys that no longer exist in the underlying storage if `sanitize()` is not called.fixAlways call `await datastore.sanitize()` after performing `remove` operations to ensure the indices are synchronized with the actual data in your storage driver.
Warnings
- gotcha When querying with date fields using comparison operators like `$gt`, `$gte`, `$lt`, or `$lte`, dates must be saved as numbers (e.g., using `Date.prototype.getTime()`) to ensure proper comparison within the index.
- breaking Older versions (prior to 0.5.0) of TeDB had a major bug in the underlying `binary-type-tree` which could cause endless appending to non-unique indices, leading to stack overflows upon loading the index after sufficient data accumulation.
- gotcha After removing documents, indices might retain references to non-existent keys. It is recommended to call `datastore.sanitize()` to clean up these orphaned index entries.
- gotcha In versions prior to 0.2.16, attempting to update a document by matching on its own `_id` could incorrectly cause the document to be removed from indices before the update was applied, leading to data inconsistencies.
Install
-
npm install tedb -
yarn add tedb -
pnpm add tedb
Imports
- Datastore
const Datastore = require('tedb').Datastore;import { Datastore } from 'tedb'; - Cursor
import * as tedb from 'tedb'; const cursor = new tedb.Cursor();
import { Cursor } from 'tedb'; - range
import { range } from 'tedb'; - * as tedb
import tedb from 'tedb';
import * as tedb from 'tedb';
Quickstart
import { Datastore } from 'tedb';
// A minimal in-memory storage driver example
class InMemoryStorageDriver {
private data: Map<string, any> = new Map();
constructor(private collectionName: string) {}
async read(key: string): Promise<any> { return this.data.get(key); }
async write(key: string, value: any): Promise<boolean> { this.data.set(key, value); return true; }
async remove(key: string): Promise<boolean> { return this.data.delete(key); }
async exists(key: string): Promise<boolean> { return this.data.has(key); }
async getAllKeys(): Promise<string[]> { return Array.from(this.data.keys()); }
}
async function runDbExample() {
const storageDriver = new InMemoryStorageDriver('myCollection');
const db = new Datastore({
storage: storageDriver,
collection: 'myCollection'
});
// Load any existing data (for persistent drivers)
await db.loadDatabase();
// Insert a document
const doc1 = await db.insert({ name: 'Alice', age: 30 });
console.log('Inserted doc1:', doc1);
// Find documents
const foundDocs = await db.find({ age: { $gt: 25 } }).exec();
console.log('Found docs (age > 25):', foundDocs);
// Update a document
const updatedCount = await db.update({ _id: doc1._id }, { $set: { age: 31 } });
console.log('Updated documents count:', updatedCount);
// Find the updated document
const updatedDoc = await db.findOne({ _id: doc1._id }).exec();
console.log('Updated doc1:', updatedDoc);
// Remove a document and sanitize indices
const removedCount = await db.remove({ _id: doc1._id });
console.log('Removed documents count:', removedCount);
await db.sanitize(); // Recommended after remove
const allDocs = await db.find({}).exec();
console.log('All remaining docs:', allDocs);
}
runDbExample().catch(console.error);