Hyperbee
Append-only B-tree on Hypercore for sorted key/value data.
Hyperbee stores sorted key/value entries in an append-only B-tree backed by Hypercore. Use it when you need ordered lookups, range scans, atomic batches, namespaces, and point-in-time snapshots over one replicated log.
Install
npm i hyperbeeQuickstart
import Hypercore from 'hypercore'
import Hyperbee from 'hyperbee'
const core = new Hypercore('./bee-storage')
const db = new Hyperbee(core, {
keyEncoding: 'utf-8',
valueEncoding: 'json'
})
await db.ready()
await db.put('config', { theme: 'dark', retries: 3 })
const entry = await db.get('config')
console.log(entry.value.theme)
await db.close()API Reference
Constructor and readiness
new Hyperbee(core, [options])
Make a new Hyperbee instance. core should be a Hypercore.
| Parameter | Type | Default |
|---|---|---|
core | Hypercore | — |
options | HyperbeeOptions | {} |
Read and diff streams sort keys by their encoded byte values, so choose keyEncoding deliberately if lexicographic order matters.
await db.ready()
Waits until internal state is loaded.
Properties and identity
db.core
The underlying Hypercore backing this bee.
- Returns:
Hypercore
db.version
Number that indicates how many modifications were made, useful as a version identifier.
- Returns:
number
db.id
String containing the id (z-base-32 of the public key) identifying this bee.
- Returns:
string
db.key
Buffer containing the public key identifying this bee.
- Returns:
Buffer
db.discoveryKey
Buffer containing a key derived from db.key.
- Returns:
Buffer
db.writable
Boolean indicating if we can put or delete data in this bee.
- Returns:
boolean
db.readable
Boolean indicating if we can read from this bee. After closing the bee this will be false.
- Returns:
boolean
Writes and batches
await db.put(key, [value], [options])
Insert a new key. Value can be optional.
| Parameter | Type | Description |
|---|---|---|
key | * | The key to insert (encoded per keyEncoding). |
value | * | The value to store; optional for key-only entries. |
options | PutOptions | — |
- Returns:
Promise<void>— Resolves once the write is committed to the core.
{
cas (prev, next) { return true }
}await db.put('number', '123', { cas })
console.log(await db.get('number')) // => { seq: 1, key: 'number', value: '123' }
await db.put('number', '123', { cas })
console.log(await db.get('number')) // => { seq: 1, key: 'number', value: '123' }
// Without cas this would have been { seq: 2, ... }, and the next { seq: 3 }
await db.put('number', '456', { cas })
console.log(await db.get('number')) // => { seq: 2, key: 'number', value: '456' }
function cas(prev, next) {
// You can use same-data or same-object lib, depending on the value complexity
return prev.value !== next.value
}The cas comparator receives the current node as prev and the potential new node as next. It runs only when the key already exists.
await db.del(key, [options])
Delete a key.
| Parameter | Type | Description |
|---|---|---|
key | * | The key to delete (encoded per keyEncoding). |
options | PutOptions | — |
- Returns:
Promise<void>— Resolves once the deletion is committed to the core.
{
cas (prev, next) { return true }
}// This won't get deleted
await db.del('number', { cas })
console.log(await db.get('number')) // => { seq: 1, key: 'number', value: 'value' }
// Change the value so the next time we try to delete it then "cas" will return true
await db.put('number', 'can-be-deleted')
await db.del('number', { cas })
console.log(await db.get('number')) // => null
function cas(prev) {
return prev.value === 'can-be-deleted'
}The delete comparator runs only when the key exists. Deleting a missing key succeeds without calling cas.
db.batch()
Make a new atomic batch that is either fully processed or not processed at all.
| Parameter | Type | Description |
|---|---|---|
opts | BatchOptions | Batch options. |
- Returns:
Batch— A new batch; queue writes on it and callflush()to commit.
const batch = db.batch()
await batch.put('key', 'value')
await batch.flush()Use batches when you need grouped writes or lower overhead for many updates.
await batch.put(key, [value], [options])
Insert a key into a batch.
| Parameter | Type | Description |
|---|---|---|
key | * | The key to insert (encoded per the batch's keyEncoding). |
value | * | The value to store (encoded per the batch's valueEncoding); optional. |
options | PutOptions | options are the same as db.put method. |
- Returns:
Promise<void>— Resolves after queuing the write inside the batch.
await batch.put('key', 'value')await batch.get(key)
Get a key, value out of a batch.
| Parameter | Type | Description |
|---|---|---|
key | * | The key to look up (encoded per the batch's keyEncoding). |
opts | GetOptions | Read options. |
- Returns:
Promise<Entry\|null>— The pending or committed entry visible within the batch, ornullwhen missing.
const entry = await batch.get('key')await batch.del(key, [options])
Delete a key into the batch.
| Parameter | Type | Description |
|---|---|---|
key | * | The key to delete (encoded per the batch's keyEncoding). |
options | PutOptions | options are the same as db.del method. |
- Returns:
Promise<void>— Resolves after queuing the delete inside the batch.
await batch.del('key')await batch.flush()
Commit the batch to the database, and releases any locks it has acquired.
- Returns:
Promise<void>— Resolves after committing the queued operations and releasing the batch lock.
await batch.flush()await batch.close()
Destroy a batch, and releases any locks it has acquired on the db.
- Returns:
Promise<void>— Resolves after discarding the batch and releasing any lock it holds.
await batch.close()Call batch.close() when you want to abort a batch without writing it.
Reads and iteration
await db.get(key)
Get a key's value. Returns null if key doesn't exists.
| Parameter | Type | Description |
|---|---|---|
key | * | The key to look up (encoded per keyEncoding). |
opts | GetOptions | Read options. |
- Returns:
Promise<Entry\|null>— The entry, ornullif the key is not present.
const entry = await db.get('key') // => { seq, key, value } or nullawait db.getBySeq(seq, [options])
Get the key and value from a block number.
| Parameter | Type | Description |
|---|---|---|
seq | number | is the Hypercore index. |
options | GetOptions | Read options. |
- Returns:
Promise<{key: *, value: *}>— The block's key and value.
const { key, value } = await db.getBySeq(1)db.createReadStream([range], [options])
Make a read stream. Sort order is based on the binary value of the keys.
| Parameter | Type | Description |
|---|---|---|
range | Range | should specify the range you want to read and looks like this |
options | ReadStreamOptions | — |
- Returns:
Readable— A stream ofEntryobjects.
await db.peek([range], [options])
Similar to doing a read stream and returning the first value, but a bit faster than that.
| Parameter | Type | Description |
|---|---|---|
range | Range | bounds to search within. |
options | ReadStreamOptions | Read options (reverse selects the last entry instead of the first). |
- Returns:
Promise<Entry\|null>— The first matching entry, ornullif the range is empty.
const first = await db.peek({ gte: 'a', lt: 'd' })History and change tracking
db.createHistoryStream([options])
Create a stream of all entries ever inserted or deleted from the db.
| Parameter | Type |
|---|---|
options | HistoryStreamOptions |
- Returns:
Readable— A stream ofEntryobjects, each with an addedtype('put'or'del').
db.createDiffStream(otherVersion, [options])
Efficiently create a stream of the shallow changes between two versions of the db.
| Parameter | Type | Description |
|---|---|---|
right | number|Hyperbee | The other version to diff against: a version number, or another bee/snapshot. |
range | Range | options are the same as db.createReadStream, except for reverse. |
opts | ReadStreamOptions | Read options. |
- Returns:
Readable— A stream of{ left, right }pairs, where each side is anEntryornull.
| Option | Default | Description |
|---|---|---|
left | Object | The entry in the db |
right | Object | The entry in otherVersion |
Causally equal entries are omitted from the diff, so the stream only yields actual changes between versions.
await db.getAndWatch(key, [options])
entryWatcher.node contains the current entry in the same format as the result of bee.get(key), and will be updated as it changes.
| Parameter | Type | Description |
|---|---|---|
key | * | The key to watch (encoded per keyEncoding). |
options | EntryWatchOptions | Watch options. |
- Returns:
Promise<EntryWatcher>— a watcher which listens to changes on the given key. - Throws: if called on a non-main Hyperbee, or if the Hyperbee is closed.
const w = await db.getAndWatch('config')
w.on('update', () => console.log(w.node.value))Listen to entryWatcher.on('update') when you need push-style notifications for a single key.
await entryWatcher.close()
Stop a single-key watcher.
- Returns: Resolves after stopping the watcher created by
db.getAndWatch(...).
await entryWatcher.close()db.watch([range])
Listens to changes that are on the optional range.
| Parameter | Type | Description |
|---|---|---|
range | Range | bounds to narrow what changes are watched; same as db.createReadStream except reverse. |
opts | WatchOptions | Watch options. |
- Returns:
Watcher— a new value after a change,currentandpreviousare snapshots that are auto-closed before next value. - Throws: if called on a non-main Hyperbee (a sub, checkout, or snapshot).
for await (const [current, previous] of watcher) {
console.log(current.version)
console.log(previous.version)
}Watchers are not supported on subs or checkouts. Use range to narrow the scope on the root bee instead.
await watcher.ready()
Wait for a range watcher to be tracking changes.
- Returns: Resolves when a range watcher has loaded and is actively tracking changes.
await watcher.ready()await watcher.close()
Stop a range watcher.
- Returns: Resolves after stopping the range watcher. Breaking out of the async iterator also stops it.
await watcher.close()Snapshots and sub-bees
db.checkout(version)
Get a read-only snapshot of a previous version.
| Parameter | Type | Default | Description |
|---|---|---|---|
version | number | — | The version (see db.version) to check out. |
opts | CheckoutOptions | {} | Snapshot options. |
- Returns:
Hyperbee— A read-only bee pinned to that version.
const old = db.checkout(5)
const entry = await old.get('key')db.snapshot()
Shorthand for getting a checkout for the current version.
| Parameter | Type | Description |
|---|---|---|
opts | CheckoutOptions | Snapshot options. |
- Returns:
Hyperbee— A read-only bee pinned to the current version.
const snap = db.snapshot()db.sub('sub-prefix', options = {})
Create a sub-database where all entries will be prefixed by a given value.
| Parameter | Type | Default | Description |
|---|---|---|---|
prefix | string|Buffer | — | The namespace prefix applied to every key. |
opts | SubOptions | {} | — |
- Returns:
Hyperbee— A bee scoped to the prefixed keyspace.
const root = new Hyperbee(core)
const sub = root.sub('a')
// In root, this will have the key ('a' + separator + 'b')
await sub.put('b', 'hello')
// Returns => { key: 'b', value: 'hello')
await sub.get('b')Sub-bees are useful when one Hyperbee needs multiple logical keyspaces without creating extra cores.
await db.getHeader([options])
options are the same as the core.get method.
| Parameter | Type |
|---|---|
options | GetOptions |
- Returns:
Promise<object>— the header contained in the first block. - Throws:
DECODING_ERRORif the stored header cannot be decoded.
const header = await db.getHeader()Replication and lifecycle
db.replicate(isInitiatorOrStream)
See more about how replicate works at core.replicate.
| Parameter | Type | Description |
|---|---|---|
isInitiator | boolean|Stream | true/false to open a fresh replication stream, or an existing stream to replicate over. |
opts | object | Replication options, forwarded to core.replicate. |
- Returns:
Stream— The replication stream.
const stream = db.replicate(true)
stream.pipe(remoteStream).pipe(stream)In larger apps, replication is often handled through a shared Corestore rather than per-bee streams.
await db.close()
Fully close this bee, including its core.
Static helpers
await Hyperbee.isHyperbee(core, [options])
This requests the first block on the core, so it can throw depending on the options.
| Parameter | Type | Description |
|---|---|---|
core | Hypercore | The core to inspect. |
options | GetOptions | options are the same as the core.get method. |
- Returns:
Promise<boolean>—trueif the core contains a Hyperbee,falseotherwise.
if (await Hyperbee.isHyperbee(core)) {
const db = new Hyperbee(core)
}Types
Entry
A single entry as returned by reads such as db.get(), db.peek(), and the
read stream. key and value are decoded with the bee's keyEncoding /
valueEncoding (raw Buffers when no encoding is set).
| Property | Type | Default | Description |
|---|---|---|---|
seq | number | — | Hypercore index (block number) at which this entry was inserted. |
key | * | — | The entry's key, decoded per keyEncoding. |
value | * | — | The entry's value, decoded per valueEncoding (null for a deletion). |
HyperbeeOptions
Options for new Hyperbee().
| Property | Type | Default | Description |
|---|---|---|---|
keyEncoding | string|object | 'binary' | Key encoding: "binary" (default), "utf-8", "ascii", "json", or an abstract-encoding instance. |
valueEncoding | string|object | 'binary' | Value encoding; same options as keyEncoding. |
PutOptions
Options for db.put(), db.del() and their batch equivalents.
| Property | Type | Default | Description |
|---|---|---|---|
cas | CASComparator | — | Compare-and-swap comparator; the write only proceeds when it returns true. |
Range
Range bounds for read, diff, and watch streams. Keys are compared by their encoded byte value.
| Property | Type | Default | Description |
|---|---|---|---|
gt | * | — | Only return keys greater than this. |
gte | * | — | Only return keys greater than or equal to this. |
lt | * | — | Only return keys less than this. |
lte | * | — | Only return keys less than or equal to this. |
ReadStreamOptions
Options for db.createReadStream() and db.peek().
| Property | Type | Default | Description |
|---|---|---|---|
reverse | boolean | false | Yield entries in reverse (descending) order. |
limit | number | -1 | Maximum number of entries to return (-1 for no limit). |
HistoryStreamOptions
Options for db.createHistoryStream().
| Property | Type | Default | Description |
|---|---|---|---|
live | boolean | false | Keep the stream open and wait for new data instead of ending. |
reverse | boolean | false | Iterate from newest to oldest. |
gte | number | — | Start at this seq (inclusive); negative values are added to the current version. |
gt | number | — | Start after this seq. |
lte | number | — | Stop at this seq (inclusive). |
lt | number | — | Stop before this seq. |
limit | number | -1 | Maximum number of entries to return (-1 for no limit). |
SubOptions
Options for db.sub().
| Property | Type | Default | Description |
|---|---|---|---|
sep | Buffer | — | Namespace separator placed between the prefix and key (defaults to the parent's separator). |
keyEncoding | string|object | — | Key encoding for the sub (defaults to the parent's). |
valueEncoding | string|object | — | Value encoding for the sub (defaults to the parent's). |
GetOptions
Options for point reads such as db.get() and db.getBySeq(). The encoding
overrides apply to this call only; remaining options are forwarded to the
underlying core.get().
| Property | Type | Default | Description |
|---|---|---|---|
keyEncoding | string|object | — | Override the key encoding for this read. |
valueEncoding | string|object | — | Override the value encoding for this read. |
wait | boolean | true | Wait for the block to download from a peer if it isn't local. |
timeout | number | 0 | Max milliseconds to wait for a block (0 = no timeout). |
BatchOptions
Options for db.batch().
| Property | Type | Default | Description |
|---|---|---|---|
keyEncoding | string|object | — | Override the key encoding for this batch. |
valueEncoding | string|object | — | Override the value encoding for this batch. |
update | boolean | true | Update the underlying core before the first operation resolves. |
WatchOptions
Options for db.watch().
| Property | Type | Default | Description |
|---|---|---|---|
keyEncoding | string|object | — | Key encoding for the yielded snapshots (defaults to the bee's). |
valueEncoding | string|object | — | Value encoding for the yielded snapshots (defaults to the bee's). |
EntryWatchOptions
Options for db.getAndWatch().
| Property | Type | Default | Description |
|---|---|---|---|
keyEncoding | string|object | — | Key encoding for the watched entry (defaults to the bee's). |
valueEncoding | string|object | — | Value encoding for the watched entry (defaults to the bee's). |
CheckoutOptions
Options for db.checkout() and db.snapshot().
| Property | Type | Default | Description |
|---|---|---|---|
keyEncoding | string|object | — | Key encoding for the snapshot (defaults to the bee's). |
valueEncoding | string|object | — | Value encoding for the snapshot (defaults to the bee's). |
Errors
Coded errors this module can throw — catch them via err.code.
| Error | Thrown when |
|---|---|
DECODING_ERROR | if the stored header cannot be decoded. |
See also
- Share append-only databases with Hyperbee—replication walkthrough with reader and writer peers.
- Work with many Hypercores using Corestore—recommended multi-core replication pattern.
- Corestore—manage many Hypercores and Hyperbees from one store.
- Hypercore—append-only log that stores Hyperbee nodes.
- Hyperdrive—filesystem abstraction that builds on Hyperbee-style metadata indexing.