hyperdb: P2P Indexable Database
Hyperdb is a JavaScript library providing a database layer designed for both peer-to-peer (P2P) and local-only data storage, leveraging the Holepunch (formerly Hypercore Protocol) ecosystem. As of version 6.6.0, it offers a schema-driven approach to defining data structures, collections, and indexes using the `Hyperschema` and `hyperdb/builder` tools. It differentiates itself by allowing developers to choose between a P2P backend (Hyperbee) for distributed, eventually consistent data, or a local, embedded database backend (RocksDB) for high-performance, single-instance storage. The project appears to be actively maintained, aligning with the broader Holepunch initiative, though a strict release cadence isn't published. Key features include declarative schema and index definitions, streaming queries, and both synchronous (`await db.get`) and asynchronous (`db.find().toArray()`) data access patterns.
Common errors
-
Error: Collection '@example/members' not found
cause The specified collection or index was not registered in the database definition, or the definition file was not correctly loaded at runtime.fixVerify that your builder script correctly registers the collection using `exampleDB.collections.register()`, runs `HyperDBBuilder.toDisk(dbBuilder)`, and that your runtime code correctly imports the generated definition via `import def from './spec/hyperdb/index.js'`. -
Error: Cannot insert, database is read-only
cause The Hyperdb instance was initialized with `writable: false` (for `.bee`) or `readOnly: true` (for `.rocks`), preventing write operations.fixWhen initializing, ensure you explicitly set `{ writable: true }` in the options for `Hyperdb.rocks()` or `Hyperdb.bee()` if you intend to perform write operations. -
TypeError: (0, _hyperdb.default) is not a constructor
cause This typically indicates a CommonJS `require` call in an ES module environment, or an incorrect named/default import for a module that exports a default.fixEnsure your runtime files are treated as ES modules (e.g., use `.mjs` extension or `"type": "module"` in `package.json`) and use `import HyperDB from 'hyperdb'`. If working with the builder, use `import HyperDBBuilder from 'hyperdb/builder'` or `const HyperDBBuilder = require('hyperdb/builder')` depending on your builder script's module type. -
No such file or directory: './spec/hyperdb/index.js'
cause The generated database definition file could not be found at the specified path, often because the builder script was not run or produced output elsewhere.fixFirst, run your `build.js` (or equivalent) script to generate the definition files. Second, confirm that the `import def from './spec/hyperdb/index.js'` path in your runtime code correctly points to the location where the definition was generated, relative to the script's execution context.
Warnings
- gotcha When using `Hyperdb.bee` for P2P databases, the `autoUpdate` option defaults to `false`. This means the database will not automatically sync with updates from the underlying Hyperbee. You must manually call `db.update()` or set `autoUpdate: true` during initialization to ensure data consistency.
- gotcha All write operations (e.g., `insert`, `delete`, `update`) are buffered and require an explicit `await db.flush()` call to be committed and persisted to the underlying storage. Forgetting to flush will result in data not being saved.
- breaking Hyperdb utilizes a mandatory builder step (using `hyperdb/builder` and `hyperschema`) to generate database definitions. Without correctly defined schemas, collections, and indexes, runtime operations will fail with 'collection not found' errors. This introduces a build-time dependency that must be managed.
- gotcha The documentation and examples show a mix of CommonJS (`require`) for builder scripts and ES Modules (`import`) for runtime usage. Mixing these in the same project or misusing them can lead to module resolution errors.
- gotcha Queries executed via `db.find()` operate on a snapshot of the database state at the moment the query is initiated. Any inserts or deletes that occur while a query stream is active will not be reflected in that particular query's results.
Install
-
npm install hyperdb -
yarn add hyperdb -
pnpm add hyperdb
Imports
- HyperDB
const HyperDB = require('hyperdb')import HyperDB from 'hyperdb'
- HyperDBBuilder
const HyperDBBuilder = require('hyperdb/builder')import HyperDBBuilder from 'hyperdb/builder'
- definition
import def from './spec/hyperdb'
import def from './spec/hyperdb/index.js'
Quickstart
// This quickstart assumes you have already run a builder script (like the one in the README)
// to generate your database definition files in './spec/hyperdb/index.js'.
import HyperDB from 'hyperdb';
import def from './spec/hyperdb/index.js'; // The generated database definition
async function run() {
// Choose your engine: .rocks for local-only, .bee for P2P
// For this example, we'll use RocksDB locally.
// Replace './my-local-db' with a suitable path for your database files.
const db = HyperDB.rocks('./my-local-db', def, { writable: true });
await db.ready(); // Ensure the database is initialized and ready for operations
// Add some entries
console.log('Inserting members...');
await db.insert('@example/members', { name: 'Alice', age: 30 });
await db.insert('@example/members', { name: 'Bob', age: 25 });
await db.insert('@example/members', { name: 'Charlie', age: 30 });
await db.flush(); // Commit changes to the database
console.log('Querying for Alice by name...');
const alice = await db.get('@example/members', { name: 'Alice' });
console.log('Found Alice:', alice);
console.log('Querying for members with names starting C or later (via index)...');
// The example index 'members-by-name' has a 'mapNameToLowerCase' helper.
const olderMembersStream = db.find('@example/members-by-name', { gte: { key: 'c' } });
const olderMembers = await olderMembersStream.toArray();
console.log('Members with names starting C or later:', olderMembers.map(m => m.name));
console.log('Getting one specific member by name using findOne...');
const bob = await db.findOne('@example/members', { name: 'Bob' });
console.log('Found Bob:', bob);
await db.close(); // Close the database connection to release resources
console.log('Database closed.');
}
run().catch(console.error);