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])
- Signature:
const db = new Hyperbee(core, [options]) - Parameters:
coreis the backingHypercore;options.keyEncodingandoptions.valueEncodingaccept'binary','utf-8','ascii','json', or another abstract encoding. - Returns: A
Hyperbeeinstance that reads and writes entries on top of the supplied core. - Example:
const db = new Hyperbee(core, { keyEncoding: 'utf-8', valueEncoding: 'json' })
Read and diff streams sort keys by their encoded byte values, so choose keyEncoding deliberately if lexicographic order matters.
await db.ready()
- Signature:
await db.ready() - Parameters: None.
- Returns: Resolves when the bee has loaded enough internal state for synchronous properties such as
db.version. - Example:
await db.ready()
Properties and identity
db.core
- Signature:
db.core - Returns: The underlying
Hypercoreinstance. - Example:
swarm.join(db.core.discoveryKey)
db.version
- Signature:
db.version - Returns: The current modification count for the bee, which is useful as a version identifier for snapshots and diffs.
- Example:
const snapshot = db.checkout(db.version)
db.id
- Signature:
db.id - Returns: A z-base-32 string derived from the public key that identifies this bee.
- Example:
console.log(db.id)
db.key
- Signature:
db.key - Returns: The public key buffer for the bee.
- Example:
console.log(db.key.toString('hex'))
db.discoveryKey
- Signature:
db.discoveryKey - Returns: A derived key suitable for peer discovery without exposing
db.key. - Example:
swarm.join(db.discoveryKey)
db.writable
- Signature:
db.writable - Returns:
truewhen this process can append writes to the backing core. - Example:
if (db.writable) await db.put('mode', 'writer')
db.readable
- Signature:
db.readable - Returns:
truewhile the bee is open and readable. - Example:
if (!db.readable) throw new Error('bee is closed')
Writes and batches
await db.put(key, [value], [options])
- Signature:
await db.put(key, [value], [options]) - Parameters:
keyuseskeyEncoding;valueis optional and usesvalueEncoding;options.cas(prev, next)can accept or reject updates when a key already exists. - Returns: Resolves when the write has been appended.
- Example:
await db.put('user:1', { name: 'Ada' })
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])
- Signature:
await db.del(key, [options]) - Parameters:
keyuseskeyEncoding;options.cas(prev)can veto the delete when the current value should be preserved. - Returns: Resolves when the delete marker has been appended.
- Example:
await db.del('user:1')
The delete comparator runs only when the key exists. Deleting a missing key succeeds without calling cas.
const batch = db.batch()
- Signature:
const batch = db.batch() - Parameters: None.
- Returns: A write batch that applies all queued operations atomically when flushed.
- Example:
const batch = db.batch()
Use batches when you need grouped writes or lower overhead for many updates.
await batch.put(key, [value], [options])
- Signature:
await batch.put(key, [value], [options]) - Parameters: Same arguments and
casbehavior asdb.put(...). - Returns: Resolves after queuing the write inside the batch.
- Example:
await batch.put('settings', { retries: 3 })
const entry = await batch.get(key)
- Signature:
const entry = await batch.get(key) - Parameters:
keyuses the batch's key encoding. - Returns: The pending or committed entry visible within the batch, or
nullwhen missing. - Example:
const entry = await batch.get('settings')
await batch.del(key, [options])
- Signature:
await batch.del(key, [options]) - Parameters: Same arguments and
casbehavior asdb.del(...). - Returns: Resolves after queuing the delete inside the batch.
- Example:
await batch.del('settings')
await batch.flush()
- Signature:
await batch.flush() - Parameters: None.
- Returns: Resolves after committing the queued operations and releasing the batch lock.
- Example:
await batch.flush()
await batch.close()
- Signature:
await batch.close() - Parameters: None.
- Returns: Resolves after discarding the batch and releasing any lock it holds.
- Example:
await batch.close()
Call batch.close() when you want to abort a batch without writing it.
Reads and iteration
const entry = await db.get(key)
- Signature:
const entry = await db.get(key) - Parameters:
keyuseskeyEncoding. - Returns:
nullwhen the key is missing, otherwise{ seq, key, value }, whereseqis the backing Hypercore block index. - Example:
const entry = await db.get('config')
const entry = await db.getBySeq(seq, [options])
- Signature:
const entry = await db.getBySeq(seq, [options]) - Parameters:
seqis a Hypercore block index;optionspass through to the underlying core lookup. - Returns:
nullwhen the block does not exist, otherwise the decoded{ key, value }entry stored at that sequence number. - Example:
const entry = await db.getBySeq(4)
const stream = db.createReadStream([range], [options])
- Signature:
const stream = db.createReadStream([range], [options]) - Parameters:
rangecan includegt,gte,lt, orlte;options.reverseflips sort order andoptions.limitcaps the number of yielded entries. - Returns: An async iterable of
{ seq, key, value }entries sorted by encoded key order. - Example:
for await (const entry of db.createReadStream({ gte: 'a', lt: 'd' })) {}
const entry = await db.peek([range], [options])
- Signature:
const entry = await db.peek([range], [options]) - Parameters: Accepts the same
rangeandoptionsasdb.createReadStream(...). - Returns: The first matching entry, or
nullif the range is empty. - Example:
const first = await db.peek({ gte: 'user:' })
History and change tracking
const stream = db.createHistoryStream([options])
- Signature:
const stream = db.createHistoryStream([options]) - Parameters:
options.livekeeps the stream open;options.reverseyields newest first;gte,gt,lte, andltbound the sequence range;limitcaps results. Negative sequence bounds are resolved relative to the current version. - Returns: An async iterable of change records shaped like
{ type, seq, key, value }, wheretypeis'put'or'del'. - Example:
for await (const entry of db.createHistoryStream({ reverse: true, limit: 1 })) {}
const stream = db.createDiffStream(otherVersion, [options])
- Signature:
const stream = db.createDiffStream(otherVersion, [options]) - Parameters:
otherVersionis the version to compare against;optionsmatchdb.createReadStream(...)exceptreverseis not supported. - Returns: An async iterable of
{ left, right }pairs sorted by key, where each side is an entry ornullwhen that side lacks the key. - Example:
for await (const diff of db.createDiffStream(previousVersion)) {}
Causally equal entries are omitted from the diff, so the stream only yields actual changes between versions.
const entryWatcher = await db.getAndWatch(key, [options])
- Signature:
const entryWatcher = await db.getAndWatch(key, [options]) - Parameters:
keyselects one entry;options.keyEncodingandoptions.valueEncodingcan override the bee defaults for the watched node. - Returns: A watcher whose
nodeproperty always contains the latest entry for that key. - Example:
const entryWatcher = await db.getAndWatch('config')
Listen to entryWatcher.on('update') when you need push-style notifications for a single key.
await entryWatcher.close()
- Signature:
await entryWatcher.close() - Parameters: None.
- Returns: Resolves after stopping the watcher created by
db.getAndWatch(...). - Example:
await entryWatcher.close()
const watcher = db.watch([range])
- Signature:
const watcher = db.watch([range]) - Parameters:
rangeaccepts the same bounds asdb.createReadStream(...), exceptreverse;keyEncodingandvalueEncodingcan override the yielded snapshot encodings. - Returns: An async iterable watcher that yields
[current, previous]snapshot pairs each time the watched range changes. - Example:
for await (const [current, previous] of db.watch({ gte: 'user:' })) {}
Watchers are not supported on subs or checkouts. Use range to narrow the scope on the root bee instead.
await watcher.ready()
- Signature:
await watcher.ready() - Parameters: None.
- Returns: Resolves when a range watcher has loaded and is actively tracking changes.
- Example:
await watcher.ready()
await watcher.close()
- Signature:
await watcher.close() - Parameters: None.
- Returns: Resolves after stopping the range watcher. Breaking out of the async iterator also stops it.
- Example:
await watcher.close()
Snapshots and sub-bees
const snapshot = db.checkout(version)
- Signature:
const snapshot = db.checkout(version) - Parameters:
versionis the modification count to read from. - Returns: A read-only
Hyperbeeview pinned to that version. - Example:
const beforeImport = db.checkout(10)
const snapshot = db.snapshot()
- Signature:
const snapshot = db.snapshot() - Parameters: None.
- Returns: A read-only checkout pinned to the current
db.version. - Example:
const current = db.snapshot()
const sub = db.sub(prefix, [options])
- Signature:
const sub = db.sub(prefix, [options]) - Parameters:
prefixbecomes the namespace prefix;options.sepsets the separator buffer;options.keyEncodingandoptions.valueEncodingcan override the parent encodings for the sub-bee. - Returns: A new
Hyperbeeview that transparently prefixes all keys in that namespace. - Example:
const users = db.sub('users')
Sub-bees are useful when one Hyperbee needs multiple logical keyspaces without creating extra cores.
const header = await db.getHeader([options])
- Signature:
const header = await db.getHeader([options]) - Parameters:
optionsare passed through to the underlyingcore.get(...)call. - Returns: The decoded header stored in the first block, or throws when the header cannot be decoded.
- Example:
const header = await db.getHeader()
Replication and lifecycle
const stream = db.replicate(isInitiatorOrStream)
- Signature:
const stream = db.replicate(isInitiatorOrStream) - Parameters: Pass the same initiator flag or replication stream you would pass to the underlying Hypercore replication API.
- Returns: The replication stream for the backing core.
- Example:
const replicationStream = db.replicate(true)
In larger apps, replication is often handled through a shared Corestore rather than per-bee streams.
await db.close()
- Signature:
await db.close() - Parameters: None.
- Returns: Resolves after closing the bee and its backing core.
- Example:
await db.close()
Static helpers
const isHyperbee = await Hyperbee.isHyperbee(core, [options])
- Signature:
const isHyperbee = await Hyperbee.isHyperbee(core, [options]) - Parameters:
coreis the candidate Hypercore;optionspass through to the first-block lookup. - Returns:
truewhen the core contains a Hyperbee header, otherwisefalse. - Example:
const ok = await Hyperbee.isHyperbee(core)
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.