Discrete types for your orbit-dbs.
$ pnpm add @constl/bohr-db
Bohr-DB brings both TypeScript and runtime-checked types to your orbit-db databases, so that you can be sure that you'll only receive values that correspond to your specified data schema.
Borh-DB uses AJV to check for data validity behind the scenes. It wraps around existing orbit-db databases with a proxy, so you can use typed Borh-DB databases as a drop-in and type-safe replacement for the original orbit-db databases in your code.
Note: KeyValue
also offers the additional property .allAsJSON()
, which returns a key, value object instead of a list of entries.
...because now your orbits can only take on deterministic values.
Borh-DB currently supports the orbit-db KeyValue
, as well as the Feed
, Set
and OrderedKeyValue
databases from @constl/orbit-db-kuiper
. Pull requests for additional db types are of course welcome!
Below are a few examples of bohr-db
with KeyValue
and Set
databases. See the test folder for examples with other orbit-db database types.
As simple example with Set
:
import { createOrbit } from "@orbitdb/core";
import { registerAll } from "@constl/orbit-db-kuiper";
import { typedSet } from "@constl/bohr-db";
// Register orbit-db-kuiper database types. IMPORTANT - must call before creating orbit instance !
registerAll();
const orbit = await createOrbit({ ipfs })
const db = await orbit.open({ type: "set" });
const typedDB = typedSet({
db,
schema: numericSchema,
}); // Is exactly the same as `db`, but now type-safe
console.log(typedDB.type) // "set"
// Add valid values
await typedDB.add(1);
await typedDB.add(2);
const all = await typedDB.all(); // [1, 2]
// Invalid values are not added
await typedDB.add("not a number") // throws both TypeScript and runtime errors !
// Even invalid values somehow added to the log (already present, or received from a peer) will not appear in the data
// Force write invalid value to underlying orbit-db database
await db.add("not a number");
await typedDB.all() // Yay !! Still [1, 2]
Any ajv
schema can be used, for more complex data types:
type structure = {
a: number;
b?: string;
};
const objectSchema: JSONSchemaType<structure> = {
type: "object",
properties: {
a: { type: "number" },
b: { type: "string", nullable: true },
},
required: ["a"],
};
const db = await orbit.open({ type: "set" });
const typedDB = typedSet({
db,
schema: objectSchema,
});
// Valid data
await typedDB.add({ a: 1, b: "c" });
// Error !!
await typedDB.add({ a: 1, b: 2 });
A more complex example with KeyValue
:
import { typedKeyValue } from "@constl/bohr-db";
type structure = { a: number, b: { c: string, d?: number } };
const schema: JSONSchemaType<Partial<structure>> = {
type: "object",
properties: {
a: { type: "number", nullable: true },
b: {
type: "object",
properties: {
c: { type: "string" },
d: { type: "number", nullable: true}
}
nullable: true,
required: []
}
},
required: [],
};
const db = await orbit.open({ type: "keyvalue" });
const typedDB = typedKeyValue({
db,
schema: objectSchema,
});
// Add valid data
await typedDB.put("a", 1);
await typedDB.put("b", { c: 1, d: "e" });
const values = await typedDB.allAsJSON();
// Invalid data
await typedDB.put("a", "text") // Error !!