wdb-sdk
Installation
yarn add wdb-sdk
Instantiation
import { DB } from 'wdb-sdk'
const db = new DB({ jwk, url, hb, id })
jwk
: signer Arweave wallet JWKurl
: DB rollup server URL (default:http://localhost:6364
)`hb
: HyperBEAM WAL node URL (default:http://localhost:10001
)`id
: DB ID, don't specify it when spawning a new DB
ready()
You can ensure the rollup server is available and ready when instantiating.
const db = await new DB({ jwk, url, hb }).ready()
ready()
will ensure ${url}/status
returns status="ok"
.
If you are the HyperBEAM node operator, you can also start a rollup node by passing true
to ready()
.
const db = await new DB({ jwk, url, hb }).ready(true)
This will ensure ${hb}/~weavedb@1.0/start
returns status=true
.
spawn()
Spawn a new DB instance with db.spawn()
, which returns a DB ID.
It spawns a new process to record WAL (Write-Ahead Logging) on the HyperBEAM node, then use the process ID to create a new DB instance on the Rollup node. The rollup node automatically bundles up queries and asyncronously dumps them to the HyperBEAM process in the background, while serving users at in-memory speed with cloud-level performance.
const id = await db.spawn()
mkdir()
Creating a dir (directory) with schema
, auth
, and name
definitions.
auth
defines custom query types and their rules such as set:user
and del:user
.
await db.mkdir({
name: "users",
schema: { type: "object", required: ["name", "age"] },
auth: [["set:user,update:user,del:user", [["allow()"]]]],
})
set()
set()
executes write queries according to the auth
rules and the schema
set on the dir
.
const res = await db.set("add:user", { name: "Bob", age: 25 })
const { success, error, result, query } = res
result
result
comes with variety of metadata.
hashpath
: hash to track verifiable compute steps (AO-Core protocol)signer
: signer of the HTTP messagemsg
: HTTP message (HTTP message signature)nonce
: nonce to prevent replay attacksop
: operation (op
=opcode
+:
+oprand
)opcode
: operation typeoperand
: custom operation namequery
: query withoutop
dir
: directory to updatebefore
: data before updateddata
: data after updatedid
: DB IDts
: timestampresult
: contains transaction index and updated keys and data in the underlying kv store
Query Types (opcode
)
There are 5 opcode
types you can specify in auth
with mkdir()
.
add
: add a doc with auto-generated docid, always add a new docset
: add a new doc with specified docid, whether or not it existsupdate
: update a doc if it doesn't exist with docid, reject if it existsupsert
: add a doc with docid if it doesn't exist, update it if it existsdel
: delete a doc with docid if it exists
Special Modifiers (_$
)
_$
provides special modifiers for field updates.
// deleting the field, this is different from assigning null
await db.update({ name: {_$: "del" }}, "users", "Bob")
// timestamp
await db.update({ date: {_$: "ts" }}, "users", "Bob")
// message signer
await db.update({ signer: {_$: "signer" }}, "users", "Bob")
You can also execute advanced logic to modify the field by defining FPJSON in an array.
// increment
await db.update({ age: {_$: ["inc"] }}, "users", "Bob")
// add
await db.update({ age: {_$: ["add", 5] }}, "users", "Bob")
// remoive items
await db.update({ favs: {_$: ["without", ["apple"]] }}, "users", "Bob")
batch()
Batch-execute multiple write queries.
const Bob = { name: "Bob", age: 20, favs: [ "apple", "orange" ]}
const Alice = { name: "Alice", age: 40, favs: [ "orange", "peach" ]}
const Beth = { name: "Mike", age: 30, favs: [ "grapes", "orange" ]}
const Mike = { name: "Mike", age: 30, favs: [ "peach", "apple" ]}
await db.batch([
[ "update:user", { favs: Bob.favs }, "users", "Bob" ],
[ "set:user", Alice, "users", "Alice" ],
[ "set:user", Beth, "users", "Beth" ],
[ "set:user", Mike, "users", "Mike" ]
])
get()
single doc
const Bob = await db.get("users", "Bob")
multiple docs
const users = await db.get("users")
sort
await db.get("users", ["age", "desc"])
// => [ Alice, Beth, Bob, Mike ]
await db.get("users", ["name", "desc"])
// => [ Mike, Bob, Beth, Alice ]
await db.get("users", ["age", "desc"], ["name", "desc"])
// => [ Alice, Mike, Beth, Bob ]
limit
await db.get("users", ["age", "desc"], 2)
// => [ Alice, Beth ]
where
==
| !=
| >
| >=
| <
| <=
| in
| not-in
| array-contains
| array-contains-any
await db.get("users", ["age", "==", 30])
// => [ Beth, Mike ]
await db.get("users", ["age", "!=", 30])
// => [ Bob, Alice ]
await db.get("users", ["age", ">", 30])
// => [ Alice ]
await db.get("users", ["age", ">=", 30])
// => [ Beth, Mike, Alice ]
await db.get("users", ["age", "<", 30])
// => [ Bob ]
await db.get("users", ["age", "<=", 30])
// => [ Bob, Beth, Mike ]
await db.get("users", ["age", "in", [20, 30]])
// => [ Bob, Beth, Mike ]
await db.get("users", ["age", "not-in", [20, 30]])
// => [ Alice ]
await db.get("users", ["favs", "array-conteins", "apple"])
// => [ Bob, Mike ]
await db.get("users", ["favs", "array-conteins-any", ["apple", "peach"])
// => [ Alice, Bob, Mike ]
skip
startAt
| startAfter
| endAt
| endBefore
await db.get("users", ["age", "asc"], ["startAt", 30])
// => [ Beth, Mike, Alice ]
await db.get("users", ["age", "asc"], ["startAfter", 30])
// => [ Alice ]
await db.get("users", ["age", "asc"], ["endAt", 30])
// => [ Bob, Beth, Mike ]
await db.get("users", ["age", "asc"], ["endBefore", 30])
// => [ Bob ]
cget()
cget()
has the same interface as get()
but it returns doc with metadata.
const { __cursor__, dir, id, data: Bob } = await db.cget("users", "Bob")
You can also use the result from cget()
as a cursor with skip operations.
const cursor = await db.cget("users", "Bob")
await db.get("users", ["age", "asc"], ["startAfter", cursor])
// => [ Beth, Mike, Alice ]
iter()
iter()
internally handles cget()
and makes pagination easier.
let { docs, next, isNext } = await db.iter("users", ["age", "asc"], 2)
// docs => [ { data: Bob }, { data: Beth } ]
while(isNext) {
;({ docs, next, isNext } = await next())
// docs => [ { data: Mike }, { data: Alice } ]
}
addIndex()
Multi-field sorting requires adding the index first.
await db.addIndex([["age", "desc"], ["name", "desc"]], "users")
await db.get("users", ["age", "desc"], ["name", "desc"])
// => [ Alice, Mike, Beth, Bob ]
removeIndex()
await db.removeIndex([["age", "desc"], ["name", "desc"]], "users")
await db.get("users", ["age", "desc"], ["name", "desc"])
// => Error