9.5 KiB
Getting Started
This guide will help you get up and running with a simple OrbitDB database that you can replicate across multiple peers.
install
Install OrbitDB and Helia:
npm i helia @orbitdb/core
Prerequisites: Helia and Libp2p
OrbitDB uses Helia for block storage and Libp2p for database synchronization. You will need to configure Helia and pass it to OrbitDB when creating a peer.
Block Storage
Helia uses memory block storage by default. This means that storage is destroyed every time your application ends and OrbitDB will no longer be able to retrieve blocks from Helia. Therefore, it is necessary to configure Helia with permanent block storage. Helia comes with a variety of storage solutions including filesystem storage, IndexDB and Level. To add one of these storage mechanisms to Helia, install the relevant package:
npm i blockstore-level
then instantiate and pass to Helia:
import { LevelBlockstore } from 'blockstore-level'
const blockstore = new LevelBlockstore('./ipfs/blocks')
const ipfs = createHelia({ blockstore })
Libp2p
OrbitDB synchronizes databases between peers using a p2p networking stack called Libp2p.
An instance of Libp2p is required by Helia which is then used by OrbitDB to synchronize database data across various networks.
A simple Node.js example might look something like:
{
peerDiscovery: [
mdns()
],
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
transports: [
tcp()
],
connectionEncrypters: [noise()],
streamMuxers: [yamux()],
services: {
identify: identify(),
pubsub: gossipsub({ allowPublishToZeroTopicPeers: true })
}
}
You can export the above configuration from an ES module:
import { tcp } from '@libp2p/tcp'
import { identify } from '@libp2p/identify'
import { gossipsub } from '@chainsafe/libp2p-gossipsub'
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { mdns } from '@libp2p/mdns'
export const Libp2pOptions = {
peerDiscovery: [
mdns()
],
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
transports: [
tcp()
],
connectionEncrypters: [noise()],
streamMuxers: [yamux()],
services: {
identify: identify(),
pubsub: gossipsub({ allowPublishToZeroTopicPeers: true })
}
}
Throughout this documentation, you will see the above Libp2p configuration imported from a file called ./config/libp2p.js, for example:
import { Libp2pOptions } from './config/libp2p.js'
Creating a standalone database
To create a database on a single machine, launch an instance of OrbitDB. Once launched, you can open a new database.
Assuming you have a Node.js development environment installed, create a new project using the command line:
mkdir orbitdb-app
cd orbitdb-app
npm init
npm i helia @orbitdb/core blockstore-level @chainsafe/libp2p-gossipsub
Create a file in your project called index.js and add the following code to it:
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB } from '@orbitdb/core'
import { LevelBlockstore } from 'blockstore-level'
import { Libp2pOptions } from './config/libp2p.js'
// Create an IPFS instance.
const blockstore = new LevelBlockstore('./ipfs/blocks')
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p, blockstore })
const orbitdb = await createOrbitDB({ ipfs })
const db = await orbitdb.open('my-db')
console.log('my-db address', db.address)
// Add some records to the db.
await db.add('hello world 1')
await db.add('hello world 2')
// Print out the above records.
console.log(await db.all())
// Close your db and stop OrbitDB and IPFS.
await db.close()
await orbitdb.stop()
await ipfs.stop()
Run index.js to create your new OrbitDB database:
node index.js
You should see the address of your new database and the records you have added to it.
Without a type, OrbitDB defaults to a database type of 'events'. To change the database type, pass the type
parameter with a valid database type.
Change:
const db = await orbitdb.open('my-db')
to:
const db = await orbitdb.open('my-documents-db', { type: 'documents' })
Also replace:
await db.add('hello world 1')
await db.add('hello world 2')
with:
await db.put({ _id: "doc1", hello: "world 1", hits: 5 })
await db.put({ _id: "doc2", hello: "world 2", hits: 2 })
Run index.js again:
node index.js
You will have a new database of type 'documents'. Note that you can create all kinds of data stores using OrbitDB. The 'documents' database allows for more complex data types such as JSON.
Replicating a database
OrbitDB's power lies in its ability to replicate data between peers distributed across multiple devices and networks; peers that may not always be connected.
To create an OrbitDB database peer, create a new project called orbitdb-peer
:
mkdir orbitdb-peer
cd orbitdb-peer
npm init
npm i helia @orbitdb/core blockstore-level @chainsafe/libp2p-gossipsub
Create a new file called index.js and paste in the following code:
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB, IPFSAccessController } from '@orbitdb/core'
import { LevelBlockstore } from 'blockstore-level'
import { Libp2pOptions } from './config/libp2p.js'
import { multiaddr } from '@multiformats/multiaddr'
const main = async () => {
// create a random directory to avoid OrbitDB conflicts.
let randDir = (Math.random() + 1).toString(36).substring(2)
const blockstore = new LevelBlockstore(`./${randDir}/ipfs/blocks`)
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p, blockstore })
const orbitdb = await createOrbitDB({ ipfs, directory: `./${randDir}/orbitdb` })
let db
if (process.argv[2] && process.argv[3]) {
await orbitdb.ipfs.libp2p.dial(multiaddr(process.argv[3]))
console.log('opening db', process.argv[2])
db = await orbitdb.open(process.argv[2])
} else {
// When we open a new database, write access is only available to the
// db creator. If we want to allow other peers to write to the database,
// they must be specified in IPFSAccessController write array param. Here,
// we simply allow anyone to write to the database. A more robust solution
// would use the OrbitDBAccessController to provide mutable, "fine-grain"
// access using grant and revoke.
db = await orbitdb.open('my-db', { AccessController: IPFSAccessController({ write: ['*']}) })
console.log('libp2p address', '(copy one of these addresses then dial into this node from the second node)', orbitdb.ipfs.libp2p.getMultiaddrs())
// Copy this output if you want to connect a peer to another.
console.log('my-db address', '(copy my db address and use when launching peer 2)', db.address)
}
db.events.on('update', async (entry) => {
// what has been updated.
console.log('update', entry.payload.value)
})
if (process.argv[2]) {
await db.add('hello from second peer')
await db.add('hello again from second peer')
} else {
// write some records
await db.add('hello from first peer')
await db.add('hello again from first peer')
}
// Clean up when stopping this app using ctrl+c
process.on('SIGINT', async () => {
// print the final state of the db.
console.log((await db.all()).map(e => e.value))
// Close your db and stop OrbitDB and IPFS.
await db.close()
await orbitdb.stop()
await ipfs.stop()
process.exit()
})
}
main()
Launch peer 1 from the terminal:
node test.js
Once launched you will see some output which may look something like this:
libp2p address (copy one of these addresses then dial into this node from the second node) [
Multiaddr(/ip4/127.0.0.1/tcp/36161/p2p/12D3KooWKFWB78Hka2uPVNYYoXfucWp6rDLsQzr5CFiP67NAo7YF),
Multiaddr(/ip4/192.168.1.22/tcp/36161/p2p/12D3KooWKFWB78Hka2uPVNYYoXfucWp6rDLsQzr5CFiP67NAo7YF),
Multiaddr(/ip4/100.64.100.6/tcp/36161/p2p/12D3KooWKFWB78Hka2uPVNYYoXfucWp6rDLsQzr5CFiP67NAo7YF)
]
my-db address (copy my db address and use when launching peer 2) /orbitdb/zdpuB2aYUCnZ7YUBrDkCWpRLQ8ieUbqJEVRZEd5aDhJBDpBqj
It contains the libp2p address and db address. You will need both of these when connecting from peer 2.
Open another terminal and launch peer 2. The command takes the form node test.js <orbitdb-address> <libp2p-address>
node test.js /orbitdb/zdpuB2aYUCnZ7YUBrDkCWpRLQ8ieUbqJEVRZEd5aDhJBDpBqj /ip4/127.0.0.1/tcp/36161/p2p/12D3KooWKFWB78Hka2uPVNYYoXfucWp6rDLsQzr5CFiP67NAo7YF
What is happening is the second peer is dialing the first peer on the /ip4/ address then opens the database.
PLEASE NOTE:
This example is using mDNS to find peers on a local network. This example will not work if each peer is on a different network and you will need to implement an alternative peer discovery mechanism to achieve connectivity. Alternatively, if the address of one of the peers is known and is accessible, the other peer can dial it manually.
These kinds of connectivity configurations are beyond the scope of OrbitDB. To find out more about connectivity in Libp2p, check out https://connectivity.libp2p.io/.
Further Reading
Databases covers database management and data entry in more detail.
Replication provides a comprehensive overview of how to perform data replication across multiple peers.