CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-level

Universal abstract-level database for Node.js and browsers

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

Level

Level is a universal database abstraction layer that automatically selects the appropriate implementation based on the runtime environment. It provides a consistent API for creating lexicographically sorted key-value databases that work seamlessly across Node.js (using LevelDB) and browsers (using IndexedDB).

Package Information

  • Package Name: level
  • Package Type: npm
  • Language: JavaScript with TypeScript definitions
  • Installation: npm install level

Core Imports

const { Level } = require('level');

For ES modules:

import { Level } from 'level';

TypeScript with generics:

import { Level } from 'level';

Basic Usage

const { Level } = require('level');

// Create a database
const db = new Level('example', { valueEncoding: 'json' });

// Add an entry with key 'a' and value 1
await db.put('a', 1);

// Add multiple entries
await db.batch([{ type: 'put', key: 'b', value: 2 }]);

// Get value of key 'a': 1
const value = await db.get('a');

// Iterate entries with keys that are greater than 'a'
for await (const [key, value] of db.iterator({ gt: 'a' })) {
  console.log(value); // 2
}

TypeScript usage with generic type parameters:

import { Level } from 'level';

// Specify types of keys and values (any, in the case of json).
// The generic type parameters default to Level<string, string>.
const db = new Level<string, any>('./db', { valueEncoding: 'json' });

// All relevant methods then use those types
await db.put('a', { x: 123 });

// Specify different types when overriding encoding per operation
await db.get<string, string>('a', { valueEncoding: 'utf8' });

// It works the same for sublevels
const abc = db.sublevel('abc');
const xyz = db.sublevel<string, any>('xyz', { valueEncoding: 'json' });

Architecture

Level implements a unified interface that automatically chooses the appropriate backend:

  • Node.js Runtime: Uses classic-level which provides LevelDB-based persistent storage
  • Browser Runtime: Uses browser-level which provides IndexedDB-based storage
  • Unified API: Both implementations extend abstract-level providing consistent methods
  • Type Safety: Full TypeScript support with generic type parameters for keys and values
  • Encoding Support: Multiple encoding options including JSON, UTF-8, and binary formats

The Level class extends AbstractLevel<string | Buffer | Uint8Array, KDefault, VDefault> and provides all the functionality from the abstract-level specification.

Capabilities

Database Constructor

Creates a new database instance or opens an existing one.

/**
 * Database constructor.
 * @param location Directory path (relative or absolute) where LevelDB will store its
 * files, or in browsers, the name of the IDBDatabase to be opened.
 * @param options Options, of which some will be forwarded to open.
 */
constructor(location: string, options?: DatabaseOptions<KDefault, VDefault>);

type DatabaseOptions<K, V> = {
  // Encoding options
  keyEncoding?: string;
  valueEncoding?: string;
  
  // Database-specific options (varies by implementation)
  createIfMissing?: boolean;
  errorIfExists?: boolean;
  compression?: boolean;
  cacheSize?: number;
  
  // Additional options from classic-level and browser-level
  [key: string]: any;
};

Database Properties

Access to database metadata and configuration.

/**
 * Location that was passed to the constructor.
 */
get location(): string;

Database Lifecycle

Control database opening and closing.

/**
 * Open the database. Called automatically by other methods if not already open.
 */
open(): Promise<void>;
open(options: OpenOptions): Promise<void>;

/**
 * Close the database. This is an async operation.
 */
close(): Promise<void>;

/**
 * Database status property.
 */
get status(): string;

type OpenOptions = {
  createIfMissing?: boolean;
  errorIfExists?: boolean;
  [key: string]: any;
};

Single Value Operations

Store, retrieve, and delete individual key-value pairs.

/**
 * Get the value of a key from the database.
 * @param key The key to retrieve
 * @returns Promise resolving to the value
 */
get(key: KDefault): Promise<VDefault>;
get<K = KDefault, V = VDefault>(key: K, options: GetOptions<K, V>): Promise<V>;

/**
 * Check if a key exists in the database.
 * @param key The key to check
 * @returns Promise resolving to boolean indicating existence
 */
has(key: KDefault): Promise<boolean>;
has<K = KDefault>(key: K, options: HasOptions<K>): Promise<boolean>;

/**
 * Put a key-value pair to the database.
 * @param key The key to store
 * @param value The value to store
 */
put(key: KDefault, value: VDefault): Promise<void>;
put<K = KDefault, V = VDefault>(key: K, value: V, options: PutOptions<K, V>): Promise<void>;

/**
 * Delete a key-value pair from the database.
 * @param key The key to delete
 */
del(key: KDefault): Promise<void>;
del<K = KDefault>(key: K, options: DelOptions<K>): Promise<void>;

type GetOptions<K, V> = {
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

type HasOptions<K> = {
  keyEncoding?: string;
  [key: string]: any;
};

type PutOptions<K, V> = {
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

type DelOptions<K> = {
  keyEncoding?: string;
  [key: string]: any;
};

Batch Operations

Retrieve multiple values or perform multiple operations atomically.

/**
 * Get the values of multiple keys from the database.
 * @param keys Array of keys to retrieve
 * @returns Promise resolving to array of values
 */
getMany(keys: KDefault[]): Promise<VDefault[]>;
getMany<K = KDefault, V = VDefault>(keys: K[], options: GetManyOptions<K, V>): Promise<V[]>;

/**
 * Check if multiple keys exist in the database.
 * @param keys Array of keys to check
 * @returns Promise resolving to array of booleans indicating existence
 */
hasMany(keys: KDefault[]): Promise<boolean[]>;
hasMany<K = KDefault>(keys: K[], options: HasManyOptions<K>): Promise<boolean[]>;

/**
 * Perform multiple operations atomically.
 * @param operations Array of operations to perform
 */
batch(operations: Array<BatchOperation<typeof this, KDefault, VDefault>>): Promise<void>;
batch<K = KDefault, V = VDefault>(operations: Array<BatchOperation<typeof this, K, V>>, options: BatchOptions<K, V>): Promise<void>;

/**
 * Create a chained batch for building multiple operations.
 * @returns ChainedBatch instance for adding operations
 */
batch(): ChainedBatch<typeof this, KDefault, VDefault>;

type GetManyOptions<K, V> = {
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

type HasManyOptions<K> = {
  keyEncoding?: string;
  [key: string]: any;
};

type BatchOptions<K, V> = {
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

type BatchOperation<TDatabase, K, V> = {
  type: 'put' | 'del';
  key: K;
  value?: V; // Required for 'put', ignored for 'del'
};

interface ChainedBatch<TDatabase, K, V> {
  put(key: K, value: V): ChainedBatch<TDatabase, K, V>;
  del(key: K): ChainedBatch<TDatabase, K, V>;
  clear(): ChainedBatch<TDatabase, K, V>;
  write(): Promise<void>;
  close(): Promise<void>;
}

Sublevel Operations

Create isolated database sections that share the same underlying storage but have separate key spaces.

/**
 * Create a sublevel that acts like a separate database with its own key space.
 * @param name Sublevel name/prefix
 * @param options Optional configuration for the sublevel
 * @returns New Level instance scoped to the sublevel
 */
sublevel<SK = KDefault, SV = VDefault>(name: string, options?: DatabaseOptions<SK, SV>): Level<SK, SV>;

Iterator Operations

Create iterators for traversing database entries with range queries and filtering.

/**
 * Create an iterator for key-value pairs.
 * @param options Iterator configuration options
 * @returns Iterator instance
 */
iterator(): Iterator<typeof this, KDefault, VDefault>;
iterator<K = KDefault, V = VDefault>(options: IteratorOptions<K, V>): Iterator<typeof this, K, V>;

/**
 * Create an iterator for keys only.
 * @param options Iterator configuration options
 * @returns KeyIterator instance
 */
keys(): KeyIterator<typeof this, KDefault>;
keys<K = KDefault>(options: KeyIteratorOptions<K>): KeyIterator<typeof this, K>;

/**
 * Create an iterator for values only.
 * @param options Iterator configuration options
 * @returns ValueIterator instance
 */
values(): ValueIterator<typeof this, KDefault, VDefault>;
values<K = KDefault, V = VDefault>(options: ValueIteratorOptions<K, V>): ValueIterator<typeof this, K, V>;

type IteratorOptions<K, V> = {
  gt?: K;        // Greater than
  gte?: K;       // Greater than or equal
  lt?: K;        // Less than
  lte?: K;       // Less than or equal
  reverse?: boolean;
  limit?: number;
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

type KeyIteratorOptions<K> = {
  gt?: K;
  gte?: K;
  lt?: K;
  lte?: K;
  reverse?: boolean;
  limit?: number;
  keyEncoding?: string;
  [key: string]: any;
};

type ValueIteratorOptions<K, V> = {
  gt?: K;
  gte?: K;
  lt?: K;
  lte?: K;
  reverse?: boolean;
  limit?: number;
  keyEncoding?: string;
  valueEncoding?: string;
  [key: string]: any;
};

interface Iterator<TDatabase, K, V> {
  next(): Promise<[K, V] | undefined>;
  all(): Promise<Array<[K, V]>>;
  close(): Promise<void>;
  [Symbol.asyncIterator](): AsyncIterableIterator<[K, V]>;
}

interface KeyIterator<TDatabase, K> {
  next(): Promise<K | undefined>;
  all(): Promise<K[]>;
  close(): Promise<void>;
  [Symbol.asyncIterator](): AsyncIterableIterator<K>;
}

interface ValueIterator<TDatabase, K, V> {
  next(): Promise<V | undefined>;
  all(): Promise<V[]>;
  close(): Promise<void>;
  [Symbol.asyncIterator](): AsyncIterableIterator<V>;
}

Clear Operations

Delete all entries or a range of entries from the database.

/**
 * Delete all entries or a range of entries from the database.
 * @param options Range and encoding options
 */
clear(): Promise<void>;
clear<K = KDefault>(options: ClearOptions<K>): Promise<void>;

type ClearOptions<K> = {
  gt?: K;        // Greater than
  gte?: K;       // Greater than or equal
  lt?: K;        // Less than
  lte?: K;       // Less than or equal
  reverse?: boolean;
  limit?: number;
  keyEncoding?: string;
  [key: string]: any;
};

Resource Management

Attach and detach resources that should be managed with the database lifecycle.

/**
 * Attach a resource to be managed by the database lifecycle.
 * @param resource Resource to attach (must have close method)
 */
attachResource(resource: any): void;

/**
 * Detach a previously attached resource.
 * @param resource Resource to detach
 */
detachResource(resource: any): void;

Feature Detection

Access to database capabilities and supported features.

/**
 * Database features and capabilities.
 */
get supports(): {
  [key: string]: boolean;
};

Encoding Utilities

Utility methods for working with encodings.

/**
 * Get or normalize key encoding.
 * @param encoding Encoding name or options
 * @returns Normalized encoding
 */
keyEncoding(): any;
keyEncoding(encoding: string | any): any;

/**
 * Get or normalize value encoding.
 * @param encoding Encoding name or options
 * @returns Normalized encoding
 */
valueEncoding(): any;
valueEncoding(encoding: string | any): any;

Advanced Operations

Advanced database operations and deferred execution.

/**
 * Defer function execution until database is in the correct state.
 * @param fn Function to defer
 * @param options Defer options
 */
defer(fn: () => void): void;
defer(fn: () => void, options: DeferOptions): void;

type DeferOptions = {
  [key: string]: any;
};

Event System

The Level class extends EventEmitter and emits various events during database operations.

/**
 * Add event listener for database events.
 * @param event Event name
 * @param listener Event handler function
 */
on(event: 'open' | 'opening' | 'close' | 'closing' | 'put' | 'del' | 'batch' | 'clear', listener: (...args: any[]) => void): this;

/**
 * Remove event listener.
 * @param event Event name
 * @param listener Event handler function
 */
off(event: 'open' | 'opening' | 'close' | 'closing' | 'put' | 'del' | 'batch' | 'clear', listener: (...args: any[]) => void): this;

/**
 * Add one-time event listener.
 * @param event Event name
 * @param listener Event handler function
 */
once(event: 'open' | 'opening' | 'close' | 'closing' | 'put' | 'del' | 'batch' | 'clear', listener: (...args: any[]) => void): this;

/**
 * Emit an event.
 * @param event Event name
 * @param args Event arguments
 */
emit(event: string, ...args: any[]): boolean;

Usage Examples

JSON Encoding

const db = new Level('my-db', { valueEncoding: 'json' });

await db.put('user:1', { name: 'Alice', age: 30 });
const user = await db.get('user:1'); // { name: 'Alice', age: 30 }

Batch Operations

// Atomic batch operations
await db.batch([
  { type: 'put', key: 'user:1', value: { name: 'Alice' } },
  { type: 'put', key: 'user:2', value: { name: 'Bob' } },
  { type: 'del', key: 'user:3' }
]);

// Chained batch operations
const batch = db.batch();
batch.put('key1', 'value1');
batch.put('key2', 'value2');
batch.del('key3');
await batch.write();

Range Queries

// Iterate over a range of keys
for await (const [key, value] of db.iterator({ gte: 'user:1', lt: 'user:9' })) {
  console.log(`${key}: ${JSON.stringify(value)}`);
}

// Get all keys starting with 'user:'
const userKeys = await db.keys({ gte: 'user:', lt: 'user;\xFF' }).all();

// Get values in reverse order
const recentValues = await db.values({ reverse: true, limit: 10 }).all();

TypeScript with Generics

interface User {
  name: string;
  email: string;
  age: number;
}

// Create typed database
const userDb = new Level<string, User>('users', { valueEncoding: 'json' });

// Type-safe operations
await userDb.put('alice', { name: 'Alice', email: 'alice@example.com', age: 30 });
const alice: User = await userDb.get('alice');

// Override types for specific operations
const rawData = await userDb.get<string, string>('alice', { valueEncoding: 'utf8' });

Sublevel Usage

const db = new Level('my-db', { valueEncoding: 'json' });

// Create sublevels for different data types
const users = db.sublevel('users');
const posts = db.sublevel('posts');

// Each sublevel acts like a separate database
await users.put('alice', { name: 'Alice', age: 30 });
await posts.put('post1', { title: 'Hello World', author: 'alice' });

// TypeScript with sublevel typing
const typedUsers = db.sublevel<string, User>('users', { valueEncoding: 'json' });

Key Existence Checking

// Check if a single key exists
const exists = await db.has('user:1');
console.log(exists); // true or false

// Check multiple keys at once
const keysExist = await db.hasMany(['user:1', 'user:2', 'user:3']);
console.log(keysExist); // [true, false, true]

Clear Operations

// Clear all entries
await db.clear();

// Clear a range of entries
await db.clear({ gte: 'user:', lt: 'user;\xFF' });

// Clear with limit
await db.clear({ limit: 100 });

Event Handling

const db = new Level('my-db', { valueEncoding: 'json' });

// Listen for database events
db.on('open', () => console.log('Database opened'));
db.on('put', (key, value) => console.log(`Put ${key}: ${JSON.stringify(value)}`));
db.on('del', (key) => console.log(`Deleted ${key}`));

// One-time listeners
db.once('close', () => console.log('Database closed'));

// Operations will emit events
await db.put('test', { data: 'example' }); // Emits 'put' event
await db.del('test'); // Emits 'del' event
await db.close(); // Emits 'close' event

Resource Management

const db = new Level('my-db');

// Create a resource that needs cleanup
const customResource = {
  close: async () => console.log('Custom resource closed')
};

// Attach resource to database lifecycle
db.attachResource(customResource);

// When database closes, attached resources are also closed
await db.close(); // Also closes customResource

Error Handling

Level operations may throw various errors:

  • NotFoundError: Thrown by get() when a key doesn't exist
  • OpenError: Thrown when database cannot be opened
  • WriteError: Thrown when write operations fail
  • IteratorError: Thrown during iterator operations
try {
  const value = await db.get('nonexistent-key');
} catch (error) {
  if (error.code === 'LEVEL_NOT_FOUND') {
    console.log('Key not found');
  }
}

Environment Compatibility

  • Node.js: 18+ with native LevelDB bindings
  • Browsers: Modern browsers with IndexedDB support (Chrome, Firefox, Safari, Edge)
  • Electron: 30+ with both Node.js and browser contexts
  • Platform Support: Linux, macOS, Windows, including ARM platforms
  • Key/Value Types: string, Buffer, Uint8Array for keys; any serializable type for values
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/level@10.0.x
Publish Source
CLI
Badge
tessl/npm-level badge