# Share append-only databases with Hyperbee



[Hyperbee](/p2p/reference/building-blocks/hyperbee) is an append-only B-tree based on [Hypercore](/p2p/reference/building-blocks/hypercore).
It provides a key/value-store API with methods to insert and get key/value pairs, perform atomic batch insertions, and create sorted iterators.
This guide uses [Corestore](/p2p/reference/helpers/corestore) and [Hyperswarm](/p2p/reference/building-blocks/hyperswarm) to manage and replicate the underlying core; see [Work with many Hypercores using Corestore](/p2p/how-to/store-and-replicate/work-with-many-hypercores-using-corestore) if those concepts are unfamiliar.

<Callout type="info">
  **Pear-end logic—start from a boilerplate.** The code in this guide lives in the Bare [worker](/pear/explanation/workers): peer-to-peer logic, no UI. The same worker runs unchanged on desktop, terminal, and mobile—only the shell differs (see [Runtime and languages](/pear/explanation/runtime-and-languages)). Start from a boilerplate in [Start from a template](/p2p/getting-started/from-a-template)—desktop ([`hello-pear-electron`](/p2p/getting-started/from-a-template/start-from-hello-pear-electron)) or terminal ([`hello-pear-bare`](/p2p/getting-started/from-a-template/start-from-hello-pear-bare))—and add this capability on top.
</Callout>

This guide consists of three applications:

* [`bee-writer-app`](#create-the-bee-writer-app) - stores 1,000 entries from a given dictionary file into a Hyperbee instance.
* [`bee-reader-app`](#create-the-bee-reader-app) - queries the Hyperbee instance for key/value pairs.
* [`core-reader-app`](#inspect-the-hyperbee-as-a-hypercore) - inspects the Hyperbee as a Hypercore.

## Create the bee writer app [#create-the-bee-writer-app]

The `bee-writer-app` stores 1,000 entries from a given dictionary file into a Hyperbee instance. The Corestore instance used to create the Hyperbee instance is replicated using Hyperswarm. This enables other peers to replicate their Corestore instance and sparsely (on-demand) download the dictionary data into their local Hyperbee instances.

### Create the `bee-writer-app` directory and add dependencies [#create-the-bee-writer-app-directory-and-add-dependencies]

Start the `bee-writer-app` project with the following commands:

```sh example=hyperbee-kv step=setup
mkdir bee-writer-app
cd bee-writer-app
npm init -y
npm pkg set type="module"
npm install corestore hyperswarm hyperbee b4a bare-fs bare-process
```

This will install the following dependencies:

* [`bare-fs`](https://www.npmjs.com/package/bare-fs): A module for working with file systems.
* [`bare-process`](https://www.npmjs.com/package/bare-process): A module for working with processes.
* [`hyperswarm`](/p2p/reference/building-blocks/hyperswarm): A module for working with Hyperswarm.
* [`corestore`](/p2p/reference/helpers/corestore): A module for working with Corestore.
* [`hyperbee`](/p2p/reference/building-blocks/hyperbee): A module for working with Hyperbee.
* [`b4a`](https://www.npmjs.com/package/b4a): A module for working with buffers.

### Add the `bee-writer-app` logic [#add-the-bee-writer-app-logic]

Create the `bee-writer-app/index.js` file with the following content:

A [Corestore](/p2p/reference/helpers/corestore) holds the backing core (L8), and a [Hyperswarm](/p2p/reference/building-blocks/hyperswarm) replicates it on every connection (L10–L14). The Hyperbee is built on a named core (L17) with UTF-8 key/value encoding (L20–L23). Once the core is ready (L26) it joins the swarm on the core's discovery key (L29), and the writable key is printed only after the topic is announced to the DHT (L32–L34). On the first run ([`core.length <= 1`](/p2p/reference/building-blocks/hypercore#corelength)), the dictionary is loaded and inserted in a single atomic batch (L38–L47); on later runs it just re-seeds the existing data (L48–L50).

```javascript file=<rootDir>/examples/how-to/store-and-replicate/share-append-only-databases-with-hyperbee/bee-writer-app/index.js title="bee-writer-app/index.js" lineNumbers {8,10-14,17,20-23,26,29,32-34,38-47} skip="example-import"
import fsp from 'bare-fs/promises'
import process from 'bare-process'
import Hyperswarm from 'hyperswarm'
import Corestore from 'corestore'
import Hyperbee from 'hyperbee'
import b4a from 'b4a'
// create a corestore instance with the given location
const store = new Corestore('./bee-writer-storage')

const swarm = new Hyperswarm()
process.once('SIGINT', () => swarm.destroy().then(() => process.exit(0), () => process.exit(0)))

// replication of corestore instance
swarm.on('connection', conn => store.replicate(conn))

// creation of Hypercore instance (if not already created)
const core = store.get({ name: 'my-bee-core' })

// creation of Hyperbee instance using the core instance 
const bee = new Hyperbee(core, {
  keyEncoding: 'utf-8',
  valueEncoding: 'utf-8'
})

// wait till all the properties of the hypercore are initialized
await core.ready()

// join a topic
const discovery = swarm.join(core.discoveryKey)

// Only display the key once the Hyperbee has been announced to the DHT
discovery.flushed().then(() => {
  console.log('bee key:', b4a.toString(core.key, 'hex'))
})

// Only import the dictionary the first time this script is executed
// The first block will always be the Hyperbee header block
if (core.length <= 1) {
  console.log('importing dictionary...')
  const dict = JSON.parse(
    await fsp.readFile(new URL('./dict.json', import.meta.url))
  )
  const batch = bee.batch()
  for (const { key, value } of dict) {
    await batch.put(key, value)
  }
  await batch.flush()
} else {
  // Otherwise just seed the previously-imported dictionary
  console.log('seeding dictionary...')
}
```

### Save the `dict.json` file [#save-the-dictjson-file]

Save the `dict.json` file to the `bee-writer-app` directory. The `dict.json` file contains 1,000 dictionary words. This file holds the data that will be stored in the Hyperbee instance, and will be imported into the Hyperbee instance if it is the first time the script is run.

<Callout type="info">
  [Click here to save `dict.json`](/dict.json).
</Callout>

### Run the `bee-writer-app` [#run-the-bee-writer-app]

In one terminal, run `bee-writer-app` with `bare`.

```sh example=hyperbee-kv step=run process=writer expect="bee key:" capture-key="bee key: ([0-9a-f]+)" timeout=60000
bare bee-writer-app
```

## Create the bee reader app [#create-the-bee-reader-app]

The `bee-reader-app` creates a `Corestore` instance and replicates it using the `Hyperswarm` instance to the same topic as `bee-writer-app`. On every word entered in the command line, it will download the respective data to the local `Hyperbee` instance.

### Create the `bee-reader-app` directory and add dependencies [#create-the-bee-reader-app-directory-and-add-dependencies]

Create the `bee-reader-app` project with the following commands:

```sh example=hyperbee-kv step=setup
mkdir bee-reader-app
cd bee-reader-app
npm init -y
npm pkg set type="module"
npm install corestore hyperswarm hyperbee b4a bare-pipe bare-process
```

This will install the following dependencies:

* [`bare-process`](https://www.npmjs.com/package/bare-process): A module for working with processes.
* [`hyperswarm`](/p2p/reference/building-blocks/hyperswarm): A module for working with Hyperswarm.
* [`corestore`](/p2p/reference/helpers/corestore): A module for working with Corestore.
* [`hyperbee`](/p2p/reference/building-blocks/hyperbee): A module for working with Hyperbee.
* [`b4a`](https://www.npmjs.com/package/b4a): A module for working with buffers.

### Add the `bee-reader-app` logic [#add-the-bee-reader-app-logic]

Create the `bee-reader-app/index.js` file with the following content:

The reader takes the writer's public key as a command-line argument (L8–L10) and opens that core through its own [Corestore](/p2p/reference/helpers/corestore) (L13), replicated over [Hyperswarm](/p2p/reference/building-blocks/hyperswarm) (L15–L19). The core is reopened from the supplied key (L22) and wrapped in a Hyperbee with matching UTF-8 encoding (L25–L28). After `ready()` (L31) it joins the same topic (L37) and reads words from stdin (L39): each query calls `bee.get(word)` and prints the value, downloading only the blocks needed to satisfy it (L41–L48).

```javascript file=<rootDir>/examples/how-to/store-and-replicate/share-append-only-databases-with-hyperbee/bee-reader-app/index.js title="bee-reader-app/index.js" lineNumbers {8-10,13,15-19,22,25-28,31,37,39,41-48} skip="example-import"
import process from 'bare-process'
import Hyperswarm from 'hyperswarm'
import Corestore from 'corestore'
import Hyperbee from 'hyperbee'
import Pipe from 'bare-pipe'
import b4a from 'b4a'

const key = Bare.argv[2]

if (!key) throw new Error('provide a key')

// creation of a corestore instance 
const store = new Corestore('./bee-reader-storage')

const swarm = new Hyperswarm()
process.once('SIGINT', () => swarm.destroy().then(() => process.exit(0), () => process.exit(0)))

// replication of the corestore instance on connection with other peers
swarm.on('connection', (conn) => store.replicate(conn))

// create or get the hypercore using the public key supplied as command-line argument
const core = store.get({ key: b4a.from(key, 'hex') })

// create a hyperbee instance using the hypercore instance
const bee = new Hyperbee(core, {
  keyEncoding: 'utf-8',
  valueEncoding: 'utf-8'
})

// wait till the hypercore properties to be initialized
await core.ready()

// logging the public key of the hypercore instance
console.log('core key here is:', core.key.toString('hex'))

// Attempt to connect to peers
swarm.join(core.discoveryKey)

const stdin = new Pipe(0)

stdin.on('data', (data) => {
  const word = data.toString().trim()
  if (!word.length) return
  bee.get(word).then(node => {
    if (!node || !node.value) console.log(`No dictionary entry for ${word}`)
    else console.log(`${word} -> ${node.value}`)
    setImmediate(console.log) // flush hack
  }, console.error)
})
```

### Run the `bee-reader-app` [#run-the-bee-reader-app]

In another terminal, run the `bee-reader-app` with `bare` and pass it the core key from the [`bee-writer-app`](#run-the-bee-writer-app).

```sh example=hyperbee-kv step=run process=reader cmd="bare bee-reader-app ${key}" expect="core key here is:" timeout=45000
bare bee-reader-app <SUPPLY THE KEY HERE>
```

Query the database by entering a key from the [`dict file`](#save-the-dictjson-file) to lookup into the `bee-reader-app` terminal and hitting return.

<Callout type="info">
  Each application has its own Corestore directory: `./bee-reader-storage` for the reader and `./bee-writer-storage` for the writer.
  Look at the disk space for the `bee-reader-app` storage path after each query and notice that it's significantly smaller than `bee-writer-app`.
  This is because Hyperbee only downloads the [Hypercore](/p2p/reference/building-blocks/hypercore) blocks it needs to satisfy each query, a feature called **sparse downloading**.
</Callout>

<Callout type="tip">
  A Hyperbee is itself a Hypercore: its B-tree nodes are stored as Hypercore blocks.
  That's what makes the next section possible—inspecting the same data as a raw Hypercore.
</Callout>

## Inspect the Hyperbee as a Hypercore [#inspect-the-hyperbee-as-a-hypercore]

### Create the `core-reader-app` directory and add dependencies [#create-the-core-reader-app-directory-and-add-dependencies]

Finally create the `core-reader-app` project with the following commands:

```sh skip="manual-walkthrough"
mkdir core-reader-app
cd core-reader-app
npm init -y
npm pkg set type="module"
npm install corestore hyperswarm hyperbee b4a bare-process
```

This will install the following dependencies:

* [`bare-process`](https://www.npmjs.com/package/bare-process): A module for working with processes.
* [`hyperswarm`](/p2p/reference/building-blocks/hyperswarm): A module for working with Hyperswarm.
* [`corestore`](/p2p/reference/helpers/corestore): A module for working with Corestore.
* [`hyperbee`](/p2p/reference/building-blocks/hyperbee): A module for working with Hyperbee.
* [`b4a`](https://www.npmjs.com/package/b4a): A module for working with buffers.

### Add the `core-reader-app` logic [#add-the-core-reader-app-logic]

Create the `core-reader-app/index.js` file with the following content:

This app treats the Hyperbee purely as a [Hypercore](/p2p/reference/building-blocks/hypercore). It imports Hyperbee's `Node` encoding directly so it can decode tree nodes (L6), takes the writer's key as an argument (L8–L10), and opens the core through a [Corestore](/p2p/reference/helpers/corestore) (L13) replicated over [Hyperswarm](/p2p/reference/building-blocks/hyperswarm) (L15–L19). The core is reopened from the key and made ready (L22–L24), then joins the topic and waits for the swarm to flush (L27–L28). After pulling the latest metadata with [`core.update()`](/p2p/reference/building-blocks/hypercore#await-coreupdateoptions) (L31), it fetches the last block by sequence number (L33–L34) and logs it both raw and decoded through `Node.decode` (L37–L38).

```javascript file=<rootDir>/examples/how-to/store-and-replicate/share-append-only-databases-with-hyperbee/core-reader-app/index.js title="core-reader-app/index.js" lineNumbers {6,8-10,13,15-19,22-24,27-28,31,33-34,37-38} skip="example-import"

import process from 'bare-process'
import Hyperswarm from 'hyperswarm'
import Corestore from 'corestore'
import b4a from 'b4a'
import { Node } from 'hyperbee/lib/messages.js'

const key = Bare.argv[2]

if (!key) throw new Error('provide a key')

// creation of a corestore instance 
const store = new Corestore('./reader-storage')

const swarm = new Hyperswarm()
process.once('SIGINT', () => swarm.destroy().then(() => process.exit(0), () => process.exit(0)))

// replication of the corestore instance on connection with other peers
swarm.on('connection', conn => store.replicate(conn))

// create or get the hypercore using the public key supplied as command-line argument
const core = store.get({ key: b4a.from(key, 'hex') })
// wait till the properties of the hypercore instance are initialized
await core.ready()

// join a topic
swarm.join(core.discoveryKey)
await swarm.flush()

// update the meta-data information of the hypercore instance
await core.update()

const seq = core.length - 1
const lastBlock = await core.get(core.length - 1)

// print the information about the last block or the latest block of the hypercore instance
console.log(`Raw Block ${seq}:`, lastBlock)
console.log(`Decoded Block ${seq}`, Node.decode(lastBlock))
```

### Run the `core-reader-app` [#run-the-core-reader-app]

In another terminal, run the `core-reader-app` with `bare`, passing the core key from the [`bee-writer-app`](#run-the-bee-writer-app):

```sh skip="manual-walkthrough"
bare core-reader-app <SUPPLY KEY HERE>
```

You can now examine the Hyperbee as if it were just a Hypercore.

The `core-reader-app` will continually download and log the last block of the Hypercore containing the Hyperbee data.
Note that these blocks are encoded using Hyperbee's `Node` encoding, which has been imported directly from `Hyperbee` for the purposes of explanation.

## See also [#see-also]

* [Create a full peer-to-peer filesystem with Hyperdrive](/p2p/how-to/stream-and-share-media/create-a-full-peer-to-peer-filesystem-with-hyperdrive)—a richer data structure built on the same Hyperbee primitive.
* [Work with many Hypercores using Corestore](/p2p/how-to/store-and-replicate/work-with-many-hypercores-using-corestore)—the Corestore patterns this guide builds on.
* [Build a multiwriter app with Autobase](/p2p/how-to/store-and-replicate/build-a-multiwriter-app-with-autobase)—let several peers write to one shared view instead of a single writer.
* [Build a multiwriter database with Autobee](/p2p/how-to/store-and-replicate/build-a-multiwriter-database-with-autobee)—give this same B-tree many writers.
* [Hyperbee reference](/p2p/reference/building-blocks/hyperbee)—full API.
* [Corestore reference](/p2p/reference/helpers/corestore)—full API for the store used to manage and replicate the backing core.
