0
# Level
1
2
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).
3
4
## Package Information
5
6
- **Package Name**: level
7
- **Package Type**: npm
8
- **Language**: JavaScript with TypeScript definitions
9
- **Installation**: `npm install level`
10
11
## Core Imports
12
13
```javascript
14
const { Level } = require('level');
15
```
16
17
For ES modules:
18
19
```javascript
20
import { Level } from 'level';
21
```
22
23
TypeScript with generics:
24
25
```typescript
26
import { Level } from 'level';
27
```
28
29
## Basic Usage
30
31
```javascript
32
const { Level } = require('level');
33
34
// Create a database
35
const db = new Level('example', { valueEncoding: 'json' });
36
37
// Add an entry with key 'a' and value 1
38
await db.put('a', 1);
39
40
// Add multiple entries
41
await db.batch([{ type: 'put', key: 'b', value: 2 }]);
42
43
// Get value of key 'a': 1
44
const value = await db.get('a');
45
46
// Iterate entries with keys that are greater than 'a'
47
for await (const [key, value] of db.iterator({ gt: 'a' })) {
48
console.log(value); // 2
49
}
50
```
51
52
TypeScript usage with generic type parameters:
53
54
```typescript
55
import { Level } from 'level';
56
57
// Specify types of keys and values (any, in the case of json).
58
// The generic type parameters default to Level<string, string>.
59
const db = new Level<string, any>('./db', { valueEncoding: 'json' });
60
61
// All relevant methods then use those types
62
await db.put('a', { x: 123 });
63
64
// Specify different types when overriding encoding per operation
65
await db.get<string, string>('a', { valueEncoding: 'utf8' });
66
67
// It works the same for sublevels
68
const abc = db.sublevel('abc');
69
const xyz = db.sublevel<string, any>('xyz', { valueEncoding: 'json' });
70
```
71
72
## Architecture
73
74
Level implements a unified interface that automatically chooses the appropriate backend:
75
76
- **Node.js Runtime**: Uses `classic-level` which provides LevelDB-based persistent storage
77
- **Browser Runtime**: Uses `browser-level` which provides IndexedDB-based storage
78
- **Unified API**: Both implementations extend `abstract-level` providing consistent methods
79
- **Type Safety**: Full TypeScript support with generic type parameters for keys and values
80
- **Encoding Support**: Multiple encoding options including JSON, UTF-8, and binary formats
81
82
The Level class extends `AbstractLevel<string | Buffer | Uint8Array, KDefault, VDefault>` and provides all the functionality from the abstract-level specification.
83
84
## Capabilities
85
86
### Database Constructor
87
88
Creates a new database instance or opens an existing one.
89
90
```typescript { .api }
91
/**
92
* Database constructor.
93
* @param location Directory path (relative or absolute) where LevelDB will store its
94
* files, or in browsers, the name of the IDBDatabase to be opened.
95
* @param options Options, of which some will be forwarded to open.
96
*/
97
constructor(location: string, options?: DatabaseOptions<KDefault, VDefault>);
98
99
type DatabaseOptions<K, V> = {
100
// Encoding options
101
keyEncoding?: string;
102
valueEncoding?: string;
103
104
// Database-specific options (varies by implementation)
105
createIfMissing?: boolean;
106
errorIfExists?: boolean;
107
compression?: boolean;
108
cacheSize?: number;
109
110
// Additional options from classic-level and browser-level
111
[key: string]: any;
112
};
113
```
114
115
### Database Properties
116
117
Access to database metadata and configuration.
118
119
```typescript { .api }
120
/**
121
* Location that was passed to the constructor.
122
*/
123
get location(): string;
124
```
125
126
### Database Lifecycle
127
128
Control database opening and closing.
129
130
```typescript { .api }
131
/**
132
* Open the database. Called automatically by other methods if not already open.
133
*/
134
open(): Promise<void>;
135
open(options: OpenOptions): Promise<void>;
136
137
/**
138
* Close the database. This is an async operation.
139
*/
140
close(): Promise<void>;
141
142
/**
143
* Database status property.
144
*/
145
get status(): string;
146
147
type OpenOptions = {
148
createIfMissing?: boolean;
149
errorIfExists?: boolean;
150
[key: string]: any;
151
};
152
```
153
154
### Single Value Operations
155
156
Store, retrieve, and delete individual key-value pairs.
157
158
```typescript { .api }
159
/**
160
* Get the value of a key from the database.
161
* @param key The key to retrieve
162
* @returns Promise resolving to the value
163
*/
164
get(key: KDefault): Promise<VDefault>;
165
get<K = KDefault, V = VDefault>(key: K, options: GetOptions<K, V>): Promise<V>;
166
167
/**
168
* Check if a key exists in the database.
169
* @param key The key to check
170
* @returns Promise resolving to boolean indicating existence
171
*/
172
has(key: KDefault): Promise<boolean>;
173
has<K = KDefault>(key: K, options: HasOptions<K>): Promise<boolean>;
174
175
/**
176
* Put a key-value pair to the database.
177
* @param key The key to store
178
* @param value The value to store
179
*/
180
put(key: KDefault, value: VDefault): Promise<void>;
181
put<K = KDefault, V = VDefault>(key: K, value: V, options: PutOptions<K, V>): Promise<void>;
182
183
/**
184
* Delete a key-value pair from the database.
185
* @param key The key to delete
186
*/
187
del(key: KDefault): Promise<void>;
188
del<K = KDefault>(key: K, options: DelOptions<K>): Promise<void>;
189
190
type GetOptions<K, V> = {
191
keyEncoding?: string;
192
valueEncoding?: string;
193
[key: string]: any;
194
};
195
196
type HasOptions<K> = {
197
keyEncoding?: string;
198
[key: string]: any;
199
};
200
201
type PutOptions<K, V> = {
202
keyEncoding?: string;
203
valueEncoding?: string;
204
[key: string]: any;
205
};
206
207
type DelOptions<K> = {
208
keyEncoding?: string;
209
[key: string]: any;
210
};
211
```
212
213
### Batch Operations
214
215
Retrieve multiple values or perform multiple operations atomically.
216
217
```typescript { .api }
218
/**
219
* Get the values of multiple keys from the database.
220
* @param keys Array of keys to retrieve
221
* @returns Promise resolving to array of values
222
*/
223
getMany(keys: KDefault[]): Promise<VDefault[]>;
224
getMany<K = KDefault, V = VDefault>(keys: K[], options: GetManyOptions<K, V>): Promise<V[]>;
225
226
/**
227
* Check if multiple keys exist in the database.
228
* @param keys Array of keys to check
229
* @returns Promise resolving to array of booleans indicating existence
230
*/
231
hasMany(keys: KDefault[]): Promise<boolean[]>;
232
hasMany<K = KDefault>(keys: K[], options: HasManyOptions<K>): Promise<boolean[]>;
233
234
/**
235
* Perform multiple operations atomically.
236
* @param operations Array of operations to perform
237
*/
238
batch(operations: Array<BatchOperation<typeof this, KDefault, VDefault>>): Promise<void>;
239
batch<K = KDefault, V = VDefault>(operations: Array<BatchOperation<typeof this, K, V>>, options: BatchOptions<K, V>): Promise<void>;
240
241
/**
242
* Create a chained batch for building multiple operations.
243
* @returns ChainedBatch instance for adding operations
244
*/
245
batch(): ChainedBatch<typeof this, KDefault, VDefault>;
246
247
type GetManyOptions<K, V> = {
248
keyEncoding?: string;
249
valueEncoding?: string;
250
[key: string]: any;
251
};
252
253
type HasManyOptions<K> = {
254
keyEncoding?: string;
255
[key: string]: any;
256
};
257
258
type BatchOptions<K, V> = {
259
keyEncoding?: string;
260
valueEncoding?: string;
261
[key: string]: any;
262
};
263
264
type BatchOperation<TDatabase, K, V> = {
265
type: 'put' | 'del';
266
key: K;
267
value?: V; // Required for 'put', ignored for 'del'
268
};
269
270
interface ChainedBatch<TDatabase, K, V> {
271
put(key: K, value: V): ChainedBatch<TDatabase, K, V>;
272
del(key: K): ChainedBatch<TDatabase, K, V>;
273
clear(): ChainedBatch<TDatabase, K, V>;
274
write(): Promise<void>;
275
close(): Promise<void>;
276
}
277
```
278
279
### Sublevel Operations
280
281
Create isolated database sections that share the same underlying storage but have separate key spaces.
282
283
```typescript { .api }
284
/**
285
* Create a sublevel that acts like a separate database with its own key space.
286
* @param name Sublevel name/prefix
287
* @param options Optional configuration for the sublevel
288
* @returns New Level instance scoped to the sublevel
289
*/
290
sublevel<SK = KDefault, SV = VDefault>(name: string, options?: DatabaseOptions<SK, SV>): Level<SK, SV>;
291
```
292
293
### Iterator Operations
294
295
Create iterators for traversing database entries with range queries and filtering.
296
297
```typescript { .api }
298
/**
299
* Create an iterator for key-value pairs.
300
* @param options Iterator configuration options
301
* @returns Iterator instance
302
*/
303
iterator(): Iterator<typeof this, KDefault, VDefault>;
304
iterator<K = KDefault, V = VDefault>(options: IteratorOptions<K, V>): Iterator<typeof this, K, V>;
305
306
/**
307
* Create an iterator for keys only.
308
* @param options Iterator configuration options
309
* @returns KeyIterator instance
310
*/
311
keys(): KeyIterator<typeof this, KDefault>;
312
keys<K = KDefault>(options: KeyIteratorOptions<K>): KeyIterator<typeof this, K>;
313
314
/**
315
* Create an iterator for values only.
316
* @param options Iterator configuration options
317
* @returns ValueIterator instance
318
*/
319
values(): ValueIterator<typeof this, KDefault, VDefault>;
320
values<K = KDefault, V = VDefault>(options: ValueIteratorOptions<K, V>): ValueIterator<typeof this, K, V>;
321
322
type IteratorOptions<K, V> = {
323
gt?: K; // Greater than
324
gte?: K; // Greater than or equal
325
lt?: K; // Less than
326
lte?: K; // Less than or equal
327
reverse?: boolean;
328
limit?: number;
329
keyEncoding?: string;
330
valueEncoding?: string;
331
[key: string]: any;
332
};
333
334
type KeyIteratorOptions<K> = {
335
gt?: K;
336
gte?: K;
337
lt?: K;
338
lte?: K;
339
reverse?: boolean;
340
limit?: number;
341
keyEncoding?: string;
342
[key: string]: any;
343
};
344
345
type ValueIteratorOptions<K, V> = {
346
gt?: K;
347
gte?: K;
348
lt?: K;
349
lte?: K;
350
reverse?: boolean;
351
limit?: number;
352
keyEncoding?: string;
353
valueEncoding?: string;
354
[key: string]: any;
355
};
356
357
interface Iterator<TDatabase, K, V> {
358
next(): Promise<[K, V] | undefined>;
359
all(): Promise<Array<[K, V]>>;
360
close(): Promise<void>;
361
[Symbol.asyncIterator](): AsyncIterableIterator<[K, V]>;
362
}
363
364
interface KeyIterator<TDatabase, K> {
365
next(): Promise<K | undefined>;
366
all(): Promise<K[]>;
367
close(): Promise<void>;
368
[Symbol.asyncIterator](): AsyncIterableIterator<K>;
369
}
370
371
interface ValueIterator<TDatabase, K, V> {
372
next(): Promise<V | undefined>;
373
all(): Promise<V[]>;
374
close(): Promise<void>;
375
[Symbol.asyncIterator](): AsyncIterableIterator<V>;
376
}
377
```
378
379
### Clear Operations
380
381
Delete all entries or a range of entries from the database.
382
383
```typescript { .api }
384
/**
385
* Delete all entries or a range of entries from the database.
386
* @param options Range and encoding options
387
*/
388
clear(): Promise<void>;
389
clear<K = KDefault>(options: ClearOptions<K>): Promise<void>;
390
391
type ClearOptions<K> = {
392
gt?: K; // Greater than
393
gte?: K; // Greater than or equal
394
lt?: K; // Less than
395
lte?: K; // Less than or equal
396
reverse?: boolean;
397
limit?: number;
398
keyEncoding?: string;
399
[key: string]: any;
400
};
401
```
402
403
### Resource Management
404
405
Attach and detach resources that should be managed with the database lifecycle.
406
407
```typescript { .api }
408
/**
409
* Attach a resource to be managed by the database lifecycle.
410
* @param resource Resource to attach (must have close method)
411
*/
412
attachResource(resource: any): void;
413
414
/**
415
* Detach a previously attached resource.
416
* @param resource Resource to detach
417
*/
418
detachResource(resource: any): void;
419
```
420
421
### Feature Detection
422
423
Access to database capabilities and supported features.
424
425
```typescript { .api }
426
/**
427
* Database features and capabilities.
428
*/
429
get supports(): {
430
[key: string]: boolean;
431
};
432
```
433
434
### Encoding Utilities
435
436
Utility methods for working with encodings.
437
438
```typescript { .api }
439
/**
440
* Get or normalize key encoding.
441
* @param encoding Encoding name or options
442
* @returns Normalized encoding
443
*/
444
keyEncoding(): any;
445
keyEncoding(encoding: string | any): any;
446
447
/**
448
* Get or normalize value encoding.
449
* @param encoding Encoding name or options
450
* @returns Normalized encoding
451
*/
452
valueEncoding(): any;
453
valueEncoding(encoding: string | any): any;
454
```
455
456
### Advanced Operations
457
458
Advanced database operations and deferred execution.
459
460
```typescript { .api }
461
/**
462
* Defer function execution until database is in the correct state.
463
* @param fn Function to defer
464
* @param options Defer options
465
*/
466
defer(fn: () => void): void;
467
defer(fn: () => void, options: DeferOptions): void;
468
469
type DeferOptions = {
470
[key: string]: any;
471
};
472
```
473
474
### Event System
475
476
The Level class extends EventEmitter and emits various events during database operations.
477
478
```typescript { .api }
479
/**
480
* Add event listener for database events.
481
* @param event Event name
482
* @param listener Event handler function
483
*/
484
on(event: 'open' | 'opening' | 'close' | 'closing' | 'put' | 'del' | 'batch' | 'clear', listener: (...args: any[]) => void): this;
485
486
/**
487
* Remove event listener.
488
* @param event Event name
489
* @param listener Event handler function
490
*/
491
off(event: 'open' | 'opening' | 'close' | 'closing' | 'put' | 'del' | 'batch' | 'clear', listener: (...args: any[]) => void): this;
492
493
/**
494
* Add one-time event listener.
495
* @param event Event name
496
* @param listener Event handler function
497
*/
498
once(event: 'open' | 'opening' | 'close' | 'closing' | 'put' | 'del' | 'batch' | 'clear', listener: (...args: any[]) => void): this;
499
500
/**
501
* Emit an event.
502
* @param event Event name
503
* @param args Event arguments
504
*/
505
emit(event: string, ...args: any[]): boolean;
506
```
507
508
## Usage Examples
509
510
### JSON Encoding
511
512
```javascript
513
const db = new Level('my-db', { valueEncoding: 'json' });
514
515
await db.put('user:1', { name: 'Alice', age: 30 });
516
const user = await db.get('user:1'); // { name: 'Alice', age: 30 }
517
```
518
519
### Batch Operations
520
521
```javascript
522
// Atomic batch operations
523
await db.batch([
524
{ type: 'put', key: 'user:1', value: { name: 'Alice' } },
525
{ type: 'put', key: 'user:2', value: { name: 'Bob' } },
526
{ type: 'del', key: 'user:3' }
527
]);
528
529
// Chained batch operations
530
const batch = db.batch();
531
batch.put('key1', 'value1');
532
batch.put('key2', 'value2');
533
batch.del('key3');
534
await batch.write();
535
```
536
537
### Range Queries
538
539
```javascript
540
// Iterate over a range of keys
541
for await (const [key, value] of db.iterator({ gte: 'user:1', lt: 'user:9' })) {
542
console.log(`${key}: ${JSON.stringify(value)}`);
543
}
544
545
// Get all keys starting with 'user:'
546
const userKeys = await db.keys({ gte: 'user:', lt: 'user;\xFF' }).all();
547
548
// Get values in reverse order
549
const recentValues = await db.values({ reverse: true, limit: 10 }).all();
550
```
551
552
### TypeScript with Generics
553
554
```typescript
555
interface User {
556
name: string;
557
email: string;
558
age: number;
559
}
560
561
// Create typed database
562
const userDb = new Level<string, User>('users', { valueEncoding: 'json' });
563
564
// Type-safe operations
565
await userDb.put('alice', { name: 'Alice', email: 'alice@example.com', age: 30 });
566
const alice: User = await userDb.get('alice');
567
568
// Override types for specific operations
569
const rawData = await userDb.get<string, string>('alice', { valueEncoding: 'utf8' });
570
```
571
572
### Sublevel Usage
573
574
```javascript
575
const db = new Level('my-db', { valueEncoding: 'json' });
576
577
// Create sublevels for different data types
578
const users = db.sublevel('users');
579
const posts = db.sublevel('posts');
580
581
// Each sublevel acts like a separate database
582
await users.put('alice', { name: 'Alice', age: 30 });
583
await posts.put('post1', { title: 'Hello World', author: 'alice' });
584
585
// TypeScript with sublevel typing
586
const typedUsers = db.sublevel<string, User>('users', { valueEncoding: 'json' });
587
```
588
589
### Key Existence Checking
590
591
```javascript
592
// Check if a single key exists
593
const exists = await db.has('user:1');
594
console.log(exists); // true or false
595
596
// Check multiple keys at once
597
const keysExist = await db.hasMany(['user:1', 'user:2', 'user:3']);
598
console.log(keysExist); // [true, false, true]
599
```
600
601
### Clear Operations
602
603
```javascript
604
// Clear all entries
605
await db.clear();
606
607
// Clear a range of entries
608
await db.clear({ gte: 'user:', lt: 'user;\xFF' });
609
610
// Clear with limit
611
await db.clear({ limit: 100 });
612
```
613
614
### Event Handling
615
616
```javascript
617
const db = new Level('my-db', { valueEncoding: 'json' });
618
619
// Listen for database events
620
db.on('open', () => console.log('Database opened'));
621
db.on('put', (key, value) => console.log(`Put ${key}: ${JSON.stringify(value)}`));
622
db.on('del', (key) => console.log(`Deleted ${key}`));
623
624
// One-time listeners
625
db.once('close', () => console.log('Database closed'));
626
627
// Operations will emit events
628
await db.put('test', { data: 'example' }); // Emits 'put' event
629
await db.del('test'); // Emits 'del' event
630
await db.close(); // Emits 'close' event
631
```
632
633
### Resource Management
634
635
```javascript
636
const db = new Level('my-db');
637
638
// Create a resource that needs cleanup
639
const customResource = {
640
close: async () => console.log('Custom resource closed')
641
};
642
643
// Attach resource to database lifecycle
644
db.attachResource(customResource);
645
646
// When database closes, attached resources are also closed
647
await db.close(); // Also closes customResource
648
```
649
650
## Error Handling
651
652
Level operations may throw various errors:
653
654
- **NotFoundError**: Thrown by `get()` when a key doesn't exist
655
- **OpenError**: Thrown when database cannot be opened
656
- **WriteError**: Thrown when write operations fail
657
- **IteratorError**: Thrown during iterator operations
658
659
```javascript
660
try {
661
const value = await db.get('nonexistent-key');
662
} catch (error) {
663
if (error.code === 'LEVEL_NOT_FOUND') {
664
console.log('Key not found');
665
}
666
}
667
```
668
669
## Environment Compatibility
670
671
- **Node.js**: 18+ with native LevelDB bindings
672
- **Browsers**: Modern browsers with IndexedDB support (Chrome, Firefox, Safari, Edge)
673
- **Electron**: 30+ with both Node.js and browser contexts
674
- **Platform Support**: Linux, macOS, Windows, including ARM platforms
675
- **Key/Value Types**: string, Buffer, Uint8Array for keys; any serializable type for values