Abstract Key-Value Database Class
abstract-level provides an abstract interface for building lexicographically sorted key-value databases, serving as the foundation for the entire LevelDB ecosystem in JavaScript/TypeScript. It defines the core API, including operations like `put`, `get`, `del`, `batch`, and iterators, along with support for encodings, sublevels, events, and hooks. The current stable version is `3.1.1`, with active development reflected in regular minor and patch releases, and major versions introducing significant breaking changes (e.g., v2 to v3). Its key differentiator is being a specification rather than an implementation, allowing concrete database modules like `level` or `classic-level` to adhere to a common, well-defined API. It ships with comprehensive TypeScript types and leverages modern JavaScript features.
Common errors
-
TypeError: db.get is not a function
cause Attempting to use callback-style `db.get('key', callback)` after upgrading to `abstract-level` v2.0.0 or later, which switched to Promises.fixChange to promise-based API: `const value = await db.get('key');` or `db.get('key').then(value => ...);`. -
Error: Iterator.seek() is not implemented
cause Your chosen LevelDB implementation does not support the mandatory `iterator.seek()` method, which became required in `abstract-level` v3.0.0.fixUpgrade your concrete LevelDB implementation (e.g., `level`, `classic-level`) to a version that satisfies `abstract-level` v3.0.0 requirements, or downgrade `abstract-level` if you cannot upgrade the implementation. -
RangeError: Invalid or unsupported Node.js version
cause `abstract-level` v3.0.0 and above requires Node.js version 18 or higher due to new language features.fixUpdate your Node.js environment to version 18.x or later. -
Property 'value' does not exist on type 'unknown'.
cause TypeScript error indicating that the generic type parameters for keys and values were not specified, leading to `unknown` types when interacting with the database.fixWhen creating your database instance, specify the generic types for keys and values, e.g., `const db = new Level<string, MyDataType>('./db', { valueEncoding: 'json' });`.
Warnings
- breaking Version 3.0.0 made `iterator.seek()` a mandatory feature. Implementations must now provide this method, and consumers relying on `abstract-level`'s interface will expect its presence.
- breaking Version 3.0.0 introduced new language features, requiring Node.js >=18. Older Node.js versions are no longer supported.
- breaking Version 2.0.0 removed callbacks in favor of Promises for all asynchronous operations. Methods like `put`, `get`, `del`, `batch`, and `close` now return Promises.
- breaking Version 2.0.0 changed the behavior for non-existent entries: `get()` now returns `undefined` instead of throwing a `NotFoundError` (or similar error) when a key is not found.
- gotcha `abstract-level` is an abstract class, not a concrete database implementation. Direct instantiation of `AbstractLevel` is not intended for end-user applications.
- gotcha TypeScript users must correctly specify generic type parameters for keys and values when instantiating `AbstractLevel` implementations and sublevels, e.g., `Level<string, MyDataType>`. Failing to do so can lead to `any` types or incorrect type inference.
Install
-
npm install abstract-level -
yarn add abstract-level -
pnpm add abstract-level
Imports
- AbstractLevel
const AbstractLevel = require('abstract-level')import { AbstractLevel } from 'abstract-level' - AbstractSublevel
import AbstractSublevel from 'abstract-level/sublevel'
import { AbstractSublevel } from 'abstract-level' - AbstractLevel
import type { AbstractLevel } from 'abstract-level'
Quickstart
import { Level } from 'level';
import type { AbstractLevel, AbstractBatchOperation } from 'abstract-level';
interface MyValue { x: number; name: string; }
async function runDbExample() {
// In a real application, replace 'level' with your chosen LevelDB implementation.
// The generic types specify key and value types.
const db: AbstractLevel<string, MyValue> = new Level<string, MyValue>('./my-db-path', {
valueEncoding: 'json' // This will automatically JSON.stringify/parse values
});
try {
console.log('Opening database...');
await db.open();
await db.put('item1', { x: 1, name: 'First Item' });
await db.put('item2', { x: 2, name: 'Second Item' });
const value1 = await db.get('item1');
console.log(`Value for item1: ${JSON.stringify(value1)}`);
// Batch operations
const batchOperations: AbstractBatchOperation<string, MyValue>[] = [
{ type: 'put', key: 'item3', value: { x: 3, name: 'Third Item' } },
{ type: 'del', key: 'item1' }
];
await db.batch(batchOperations);
const value3 = await db.get('item3');
console.log(`Value for item3: ${JSON.stringify(value3)}`);
const deletedValue = await db.get('item1');
console.log(`Value for item1 after delete (should be undefined): ${deletedValue}`);
console.log('Iterating through remaining items:');
for await (const [key, value] of db.iterator()) {
console.log(`Key: ${key}, Value: ${JSON.stringify(value)}`);
}
} catch (error) {
console.error('Database operation failed:', error);
} finally {
console.log('Closing database...');
await db.close();
}
}
runDbExample();