SQLite-backed Abstract LevelDB
sqlite-level is a Node.js library that provides an `abstract-level` compliant interface, backed by a SQLite database. It leverages `better-sqlite3` for its underlying SQLite operations, offering a synchronous-like API while maintaining `abstract-level` compatibility. This allows developers familiar with the LevelDB API to utilize a durable, file-based store without needing to manage separate database processes or complex setups typical of other embedded databases. Currently stable at version 1.2.1, its release cadence is tied to the needs of the TinaCMS project, ensuring ongoing maintenance and updates. A key differentiator is its use of SQLite, offering ACID properties and wide tooling support, contrasting with LevelDB's LSM-tree design. It's particularly useful for server-side applications requiring a lightweight, transactional key-value store with data persistence for development or smaller deployments.
Common errors
-
Error [ERR_REQUIRE_ESM]: require() of ES Module ...sqlite-level/build/index.js from ... not supported.
cause Attempting to `require()` an ESM-only package from a CommonJS module.fixChange your importing code from `const { Level } = require('sqlite-level');` to `import { Level } from 'sqlite-level';`. Ensure your `package.json` for the consuming project has `"type": "module"` or dynamically import with `const { Level } = await import('sqlite-level');`. -
Error: SQLITE_BUSY: database is locked
cause Multiple concurrent write operations or an active read transaction holding a lock prevents other operations from proceeding.fixThis often occurs in concurrent environments. Implement basic retry logic with a short delay for write operations. Ensure database connections are managed properly and closed when not in use. Consider using WAL mode if not already active. -
Error: Database is closed
cause An attempt was made to perform an operation (put, get, iterate) on a `Level` instance that has already been `close()`d or failed to `open()`.fixEnsure all database operations are completed before `db.close()` is called. Use `db.isOpen()` to check the database state if you suspect it might be closed prematurely. Wrap operations in `try...finally` to ensure `db.close()` is called only when needed.
Warnings
- gotcha Installation issues can occur due to the native bindings of `better-sqlite3`, which `sqlite-level` relies on. Users may encounter compilation errors on certain platforms or specific Node.js versions.
- gotcha As `sqlite-level` uses SQLite internally, it inherits SQLite's concurrency model. While WAL (Write-Ahead Logging) can improve read concurrency, writes are still serialized. Aggressive concurrent writes can lead to `SQLITE_BUSY` errors if not handled properly.
- breaking `sqlite-level` is distributed as an ECMAScript Module (ESM). Direct `require()` statements in CommonJS environments will fail with a runtime error, indicating it's an ESM module.
- gotcha Forgetting to `await db.close()` can lead to data corruption or locks on the SQLite database file, especially if the application exits abruptly. This is critical for persistent, file-based databases.
Install
-
npm install sqlite-level -
yarn add sqlite-level -
pnpm add sqlite-level
Imports
- Level
const { Level } = require('sqlite-level');import { Level } from 'sqlite-level'; - LevelOptions
import { LevelOptions } from 'sqlite-level';import type { LevelOptions } from 'sqlite-level';
Quickstart
import { Level } from 'sqlite-level';
import { join } from 'path';
import { tmpdir } from 'os';
async function runExample() {
const dbPath = join(tmpdir(), 'my_sqlite_level_db.sqlite');
const db = new Level(dbPath);
try {
console.log(`Opening database at: ${dbPath}`);
await db.open();
console.log('Database opened.');
await db.put('name', 'Alice');
await db.put('age', '30');
const name = await db.get('name');
const age = await db.get('age');
console.log(`Name: ${name}, Age: ${age}`);
// Iterate over entries
console.log('Iterating over entries:');
for await (const [key, value] of db.iterator({ gte: 'a', lt: 'z' })) {
console.log(` Key: ${key}, Value: ${value}`);
}
await db.del('age');
const newAge = await db.get('age');
console.log(`Age after deletion: ${newAge}`); // Should be undefined
} catch (error) {
console.error('An error occurred:', error);
} finally {
if (!db.isClosed()) {
await db.close();
console.log('Database closed.');
}
}
}
runExample();