Node.js LMDB Binding
node-lmdb provides a high-performance Node.js binding to LMDB (Lightning Memory-Mapped Database), a transactional key-value store renowned for its speed and efficiency. It operates as an in-process, zero-copy database, eliminating the overhead of socket communication. The library supports transactions, multiple databases within a single environment, and is designed for multi-threaded and multi-process use, offering crash-proof persistence. The current stable version is 0.10.1, with releases occurring periodically to address bugs and introduce features. Its key differentiators include direct memory-mapped access, support for binary and string values via Node.js Buffers, and an API designed to align with JavaScript conventions while maintaining parity with the underlying LMDB C API. It's suitable for applications requiring extremely fast, durable, local data storage.
Common errors
-
MDB_MAP_FULL: Environment mapsize limit reached
cause The configured mapSize for the LMDB environment is too small to accommodate new data or existing data growth.fixIncrease the `mapSize` property in `env.open()` to a sufficiently large value. You may need to close and reopen the environment for the change to take effect. -
Error: MDB_BAD_TXN: Transaction has been aborted or is inactive
cause Attempting to use a transaction object after it has been committed or aborted, or after the environment/database has been closed.fixEnsure all database operations are performed within the scope of an active transaction. Do not reuse `Txn` objects after `commit()` or `abort()`. Create a new transaction for each logical unit of work. -
Error: MDB_NOTFOUND: No matching key/data pair found
cause An attempt was made to retrieve a key that does not exist in the specified database.fixThis is often expected behavior. Check the return value of `get*()` methods, which will typically be `null` or `undefined` if the key is not found. Handle these cases gracefully in your application logic. -
node-gyp rebuild failed
cause During `npm install`, the native C/C++ binding compilation failed due to missing build tools or incorrect environment setup.fixInstall necessary build tools for your operating system. For Windows, install Visual Studio Build Tools. For Linux, install `build-essential` (Debian/Ubuntu) or `Development Tools` (Fedora/RHEL). Ensure Python 3 is installed and configured correctly for `node-gyp`.
Warnings
- gotcha Always explicitly close transactions using `commit()` or `abort()`. Failure to do so can lead to resource leaks, database corruption, or deadlocks, especially in environments with many concurrent operations.
- breaking Versions prior to 0.7.0 might have different API signatures or less stable behavior. While specific breaking changes are not fully documented in the README, it's generally recommended to upgrade to the latest 0.10.x series for stability and features.
- gotcha The `mapSize` option in `env.open()` sets the maximum size the database file can ever grow to. If your data exceeds this, subsequent writes will fail with an `MDB_MAP_FULL` error.
- gotcha Node-lmdb is a native binding, meaning it compiles C/C++ code during installation. This can cause issues on systems without proper build tools (e.g., Python, C++ compiler).
Install
-
npm install node-lmdb -
yarn add node-lmdb -
pnpm add node-lmdb
Imports
- Env
const Env = require('node-lmdb').Env;import { Env } from 'node-lmdb'; - Txn
const Txn = require('node-lmdb');import { Txn } from 'node-lmdb'; - * as lmdb
const lmdb = require('node-lmdb').default;import * as lmdb from 'node-lmdb';
Quickstart
import { Env } from 'node-lmdb';
import * as fs from 'fs';
import * as path from 'path';
const dbPath = path.join(__dirname, 'mydata');
if (!fs.existsSync(dbPath)) {
fs.mkdirSync(dbPath);
}
const env = new Env();
env.open({
path: dbPath,
mapSize: 2 * 1024 * 1024 * 1024, // 2GB maximum database size
maxDbs: 5 // Allow up to 5 named databases
});
const dbi = env.openDbi({
name: 'myPrettyDatabase',
create: true // Create the database if it doesn't exist
});
try {
let txn = env.beginTxn();
const key1 = 1;
let value1 = txn.getString(dbi, key1);
console.log(`Initial value for key ${key1}: ${value1}`);
if (value1 === null) {
txn.putString(dbi, key1, "Hello from node-lmdb!");
console.log(`Set value for key ${key1} to 'Hello from node-lmdb!'`);
} else {
txn.del(dbi, key1);
console.log(`Deleted value for key ${key1}`);
}
const key2 = 'my_string_key';
txn.putBinary(dbi, key2, Buffer.from('Binary data example'));
console.log(`Put binary data for key '${key2}'`);
txn.commit(); // Commit the transaction
// Read the binary data back in a new transaction
txn = env.beginTxn();
const binaryValue = txn.getBinary(dbi, key2);
console.log(`Retrieved binary data for key '${key2}': ${binaryValue?.toString()}`);
txn.abort(); // Abort a read-only transaction
} catch (e) {
console.error('LMDB operation failed:', e);
} finally {
dbi.close();
env.close();
fs.rmSync(dbPath, { recursive: true, force: true }); // Clean up
}