JavaScript SDK for the Swarm decentralised storage.
Supports Node.js 18+, Vite and Webpack.
Write your code in CJS, MJS or TypeScript.
Intended to be used with Bee version 2.8.1.
Start a Swarm project using TypeScript:
npm init swarm-app@latest my-dapp node-tsor using Vite and TypeScript:
npm init swarm-app@latest my-dapp vite-tsxSupported types are node, node-esm, node-ts and vite-tsx. Replace my-dapp with your project name.
npm install @ethersphere/bee-jsconst { Bee } = require('@ethersphere/bee-js')import { Bee } from '@ethersphere/bee-js'Loading this module through a script tag will make the BeeJs object available in the global namespace.
<script src="https://unpkg.com/@ethersphere/bee-js/dist/index.browser.min.js"></script>NumberString is a branded type for marking strings that represent numbers. It interops with string and bigint
types. Where NumberString is present, number is disallowed in order to avoid pitfalls with unsafe large values.
All the classes below extend Bytes, therefore the following methods are available on all of them: toUint8Array,
toHex, toBase64, toBase32, toUtf8, toJSON, static keccak256, static fromUtf8.
The toString method uses toHex.
Bytes and its subclasses may be constructed with new from Uint8Array or hex string.
| Name | Description | Methods |
|---|---|---|
| PrivateKey | 32 bytes private key | publicKey, sign |
| PublicKey | 64 bytes public key | address, toCompressedUint8Array, toCompressedHex |
| EthAddress | 20 bytes Ethereum address | toChecksum |
| Signature | 65 bytes signature | recoverPublicKey |
| Name | Description | Methods |
|---|---|---|
| Reference | 32/64 bytes reference (chunk, feed) | toCid |
| Identifier | 32 bytes identifier (SOC, Feed) | - |
| TransactionId | 32 bytes transaction ID | - |
| FeedIndex | 8 bytes feed index (BE) | static fromBigInt, toBigInt |
| Topic | 32 bytes topic | static fromString |
| PeerAddress | 32 bytes peer address | - |
| BatchId | 32 bytes batch ID | - |
| Span | 8 bytes span (LE) | static fromBigInt, toBigInt |
| Name | Description | Methods |
|---|---|---|
| DAI | ERC20 DAI token (18 digits) | static fromDecimalString, static fromWei, toWeiString, toWeiBigInt, toDecimalString |
| BZZ | ERC20 BZZ token (16 digits) | static fromDecimalString, static fromPLUR, toPLURString, toPLURBigInt, toDecimalString |
| Name | Description | Creation |
|---|---|---|
| Chunk | Span, max. 4096 bytes payload; address derived from content | makeContentAddressedChunk |
| SingleOwnerChunk | Identifier, signature, span, max. 4096 bytes payload; address derived from identifier and owner | makeSingleOwnerChunk |
| Name | Description | Methods |
|---|---|---|
| MantarayNode | Compact trie with reference values and JSON metadata | addFork, removeFork, calculateSelfAddress, find, findClosest, collect, marshal, unmarshal, saveRecursively, loadRecursively |
| ChunkSplitter | Streaming BMT chunk-tree builder | append, finalize, static root |
| ChunkJoiner | Reconstructs data from a chunk tree | join, static collect |
| Name | Description | Creation |
|---|---|---|
| SOCWriter | SingleOwnerChunk writer | bee.soc.makeWriter |
| SOCReader | SingleOwnerChunk reader | bee.soc.makeReader |
| FeedWriter | Feed writer | bee.feed.makeWriter |
| FeedReader | Feed reader | bee.feed.makeReader |
| RollingFeedWriter | Rolling feed writer | bee.rollingFeed.makeWriter |
| RollingFeedReader | Rolling feed reader | bee.rollingFeed.makeReader |
- ❌❌✅ - Full node only
- ❌✅✅ - Light node and full node
- ✅✅✅ - Ultra-light node, light node and full node
| JS Call | Bee Endpoint | Bee Mode |
|---|---|---|
file.upload |
POST /bzz 🔗 |
❌✅✅ |
collection.uploadFromDirectory Node.js |
POST /bzz 🔗 |
❌✅✅ |
collection.uploadFromFileList |
POST /bzz 🔗 |
❌✅✅ |
collection.upload |
POST /bzz 🔗 |
❌✅✅ |
data.upload |
POST /bytes 🔗 |
❌✅✅ |
chunk.upload |
POST /chunks 🔗 |
❌✅✅ |
collection.streamFromDirectory Node.js |
POST /chunks 🔗 |
❌✅✅ |
collection.stream Browser |
POST /chunks 🔗 |
❌✅✅ |
SOCWriter.upload |
POST /soc/:owner/:identifier 🔗 |
❌✅✅ |
FeedReader.download |
GET /feeds/:owner/:topic 🔗 |
✅✅✅ |
FeedWriter.uploadReference |
POST /soc/:owner/:identifier 🔗 |
❌✅✅ |
file.download |
GET /bzz/:reference 🔗 |
✅✅✅ |
file.download |
GET /bzz/:reference/:path 🔗 |
✅✅✅ |
file.downloadReadable |
GET /bzz/:reference 🔗 |
✅✅✅ |
data.download |
GET /bytes/:reference 🔗 |
✅✅✅ |
data.downloadReadable |
GET /bytes/:reference 🔗 |
✅✅✅ |
chunk.download |
GET /chunks/:reference 🔗 |
✅✅✅ |
feed.createManifest |
POST /feeds/:owner/:topic 🔗 |
❌✅✅ |
connectivity.isConnected |
GET / |
✅✅✅ |
status.getHealth |
GET /health 🔗 |
✅✅✅ |
status.getReadiness |
GET /readiness 🔗 |
✅✅✅ |
status.getNodeInfo |
GET /node 🔗 |
✅✅✅ |
status.getChainState |
GET /chainstate 🔗 |
❌✅✅ |
stake.getRedistributionState |
GET /redistributionstate 🔗 |
❌❌✅ |
status.getReserveState |
GET /reservestate 🔗 |
❌❌✅ |
status.get |
GET /status 🔗 |
✅✅✅ |
wallet.getBalance |
GET /wallet 🔗 |
❌✅✅ |
connectivity.getTopology |
GET /topology 🔗 |
✅✅✅ |
connectivity.getNodeAddresses |
GET /addresses 🔗 |
✅✅✅ |
connectivity.getPeers |
GET /peers 🔗 |
✅✅✅ |
balance.getAll |
GET /balances 🔗 |
❌✅✅ |
balance.getPeer |
GET /balances/:peer 🔗 |
❌✅✅ |
balance.getAllPastDueConsumption |
GET /consumed 🔗 |
❌✅✅ |
balance.getAllPastDueConsumptionForPeer |
GET /consumed/:peer 🔗 |
❌✅✅ |
settlement.getAll |
GET /settlements 🔗 |
❌✅✅ |
settlement.get |
GET /settlements/:peer 🔗 |
❌✅✅ |
chequebook.getAddress |
GET /chequebook/address 🔗 |
❌✅✅ |
chequebook.getBalance |
GET /chequebook/balance 🔗 |
❌✅✅ |
cheque.getAllLatest |
GET /chequebook/cheque 🔗 |
❌✅✅ |
cheque.getAllLatestForPeer |
GET /chequebook/cheque/:peer 🔗 |
❌✅✅ |
cheque.getLastCashoutAction |
GET /chequebook/cashout/:peer 🔗 |
❌✅✅ |
cheque.cashoutLast |
POST /chequebook/cashout/:peer 🔗 |
❌✅✅ |
chequebook.deposit |
POST /chequebook/deposit 🔗 |
❌✅✅ |
chequebook.withdraw |
POST /chequebook/withdraw 🔗 |
❌✅✅ |
transaction.getAll |
GET /transactions 🔗 |
❌✅✅ |
transaction.get |
GET /transactions/:id 🔗 |
❌✅✅ |
transaction.rebroadcast |
POST /transactions/:id 🔗 |
❌✅✅ |
transaction.cancel |
DELETE /transactions/:id 🔗 |
❌✅✅ |
tag.create |
POST /tags 🔗 |
❌✅✅ |
tag.get |
GET /tags/:id 🔗 |
❌✅✅ |
tag.getAll |
GET /tags 🔗 |
❌✅✅ |
tag.delete |
DELETE /tags/:id 🔗 |
❌✅✅ |
tag.update |
PATCH /tags/:id 🔗 |
❌✅✅ |
pin.add |
POST /pins/:reference 🔗 |
✅✅✅ |
pin.getAll |
GET /pins 🔗 |
✅✅✅ |
pin.get |
GET /pins/:reference 🔗 |
✅✅✅ |
data.isRetrievable |
GET /stewardship/:reference 🔗 |
✅✅✅ |
pin.reuploadData |
PUT /stewardship/:reference 🔗 |
❌✅✅ |
pin.remove |
DELETE /pins/:reference 🔗 |
✅✅✅ |
grantee.get |
GET /grantee/:reference 🔗 |
❌✅✅ |
grantee.create |
POST /grantee 🔗 |
❌✅✅ |
grantee.patch |
PATCH /grantee/:reference 🔗 |
❌✅✅ |
messaging.pssSend |
POST /pss/send/:topic/:target 🔗 |
❌✅✅ |
messaging.pssSubscribe Websocket |
GET /pss/subscribe/:topic 🔗 |
❌❌✅ |
messaging.pssReceive |
GET /pss/subscribe/:topic 🔗 |
❌❌✅ |
stamp.getAll |
GET /stamps 🔗 |
❌✅✅ |
stamp.getAllGlobal |
GET /batches 🔗 |
❌✅✅ |
stamp.get |
GET /stamps/:batchId 🔗 |
❌✅✅ |
stamp.getBuckets |
GET /stamps/:batchId/buckets 🔗 |
❌✅✅ |
stamp.create |
POST /stamps/:amount/:depth 🔗 |
❌✅✅ |
stamp.topUp |
PATCH /stamps/topup/:batchId/:amount 🔗 |
❌✅✅ |
stamp.dilute |
PATCH /stamps/dilute/:batchId/:depth 🔗 |
❌✅✅ |
createEnvelope |
POST /envelope/:reference 🔗 |
❌✅✅ |
stake.get |
GET /stake 🔗 |
❌❌✅ |
stake.deposit |
POST /stake 🔗 |
❌❌✅ |
getCollectionSizegetFolderSize
makeMaxTarget
approximateOverheadForRedundancyLevelgetRedundancyStatgetRedundancyStats
getAmountForDurationgetDepthForSizegetStampCostgetStampEffectiveBytesgetStampTheoreticalBytesgetStampDurationgetStampUsage
import { Bee, NULL_STAMP, SWARM_GATEWAY_URL } from '@ethersphere/bee-js'
main()
async function main() {
const bee = new Bee(SWARM_GATEWAY_URL)
const { reference } = await bee.data.upload(NULL_STAMP, 'Hello, World!')
console.log(reference.toHex())
}Swarm incentivizes nodes in the network to store content, therefore all uploads require a paid postage batch.
import { Bee } from '@ethersphere/bee-js'
async function getOrCreatePostageBatch() {
const bee = new Bee('http://localhost:1633')
let batchId
const batches = await bee.stamp.getAll()
const usable = batches.find(x => x.usable)
if (usable) {
batchId = usable.batchID
} else {
batchId = await bee.storage.buy(Size.fromGigabytes(1), Duration.fromDays(7))
}
}The following examples all assume an existing batchId.
import { Bee } from '@ethersphere/bee-js'
const bee = new Bee('http://localhost:1633')
const uploadResult = await bee.data.upload(batchId, 'Bee is awesome!')
const data = await bee.data.download(uploadResult.reference)
console.log(data.toUtf8()) // prints 'Bee is awesome!'import { Bee } from '@ethersphere/bee-js'
const bee = new Bee('http://localhost:1633')
const result = await bee.file.upload(batchId, file)import { Bee } from '@ethersphere/bee-js'
const bee = new Bee('http://localhost:1633')
const result = await bee.collection.uploadFromFileList(batchId, fileList)import { Bee } from '@ethersphere/bee-js'
import { createReadStream } from 'fs'
const bee = new Bee('http://localhost:1633')
const readable = createReadStream('./path/to/large.bin')
const uploadResult = await bee.file.upload(batchId, readable)import { Bee } from '@ethersphere/bee-js'
import { createReadStream } from 'fs'
const bee = new Bee('http://localhost:1633')
const uploadResult = await bee.collection.uploadFromDirectory(batchId, './path/to/gallery/')A rolling feed avoids the unbounded growth of a plain sequential feed by restarting it every
periodLength seconds, so old postage-batch eviction never breaks the latest update. See
ROLLING_FEED.md for the full design.
A rolling feed only stays readable while the writer keeps publishing. Keeping it alive is the application's job, not the SDK's, so a writer belongs on a timer — republish on every tick, even when the data has not changed.
import { Bee, PrivateKey, Topic } from '@ethersphere/bee-js'
const bee = new Bee('http://localhost:1633')
const topic = Topic.fromString('my-feed')
const signer = new PrivateKey('...')
const periodLength = 600 // 10 minutes
const writer = bee.rollingFeed.makeWriter(topic, signer, periodLength)
let latest = 'Hello, World!'
await writer.uploadPayload(batchId, latest)
// tick twice per period so a slow or delayed write still lands inside its own period
const handle = setInterval(async () => {
try {
await writer.uploadPayload(batchId, latest)
} catch (error) {
console.error('rolling feed heartbeat failed', error)
}
}, (periodLength / 2) * 1000)
process.on('SIGTERM', () => clearInterval(handle))If the writer was down long enough to leave gaps behind, call writer.catchUp(batchId) before
resuming the timer to backfill the missed periods. It throws when no populated period is found
within maxBackfill, so it is for restarts, not for the very first run.
import { Bee, EthAddress, Topic } from '@ethersphere/bee-js'
const bee = new Bee('http://localhost:1633')
const topic = Topic.fromString('my-feed')
const owner = new EthAddress('...')
const periodLength = 600 // must match the writer
const reader = bee.rollingFeed.makeReader(topic, owner, periodLength)
const result = await reader.downloadPayload()
console.log(result.payload.toUtf8()) // prints 'Hello, World!'const bee = new Bee('http://localhost:1633', {
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
headers: {
Authorization: 'Basic ' + Buffer.from('username:password').toString('base64'),
},
})Stay up to date by joining the official Discord and by keeping an eye on the releases tab.
We are using Conventional Commits for our commit messages and pull requests, following the Semantic Versioning rules.
There are some ways you can make this module better:
- Consult our open issues and take on one of them
- Help our tests reach 100% coverage!
- Join us in our Discord chat in the #develop-on-swarm channel if you have questions or want to give feedback
Install project dependencies:
npm installBuild the project:
npm run buildAfter making changes, link the package to your project by running npm link in the Bee-JS project root, and
npm link @ethersphere/bee-js in your project root.
Tests are currently run against a mainnet Bee node. This is temporary and this section will be revised in the future.