@voiddb/orm
v1.2.3
Published
Official TypeScript ORM for VoidDB - type-safe document store client
Maintainers
Readme
Why use it
@voiddb/orm is built for the same workflow that makes Prisma productive:
- one command to scaffold local schema/config folders
- live schema pull and push from a running database
- short migration commands
- generated TypeScript types
- typed query builder with raw JSON export when you need it
- a client that can boot directly from environment variables
- direct file upload into
Blobfields without hand-writing S3 refs
It stays close to the VoidDB HTTP API, so debugging stays simple.
Install
npm install @voiddb/ormor
bun add @voiddb/ormQuick start
import { VoidClient, query } from "@voiddb/orm";
type User = {
_id: string;
name: string;
age: number;
active: boolean;
};
const client = VoidClient.fromEnv();
await client.login(process.env.VOIDDB_USERNAME!, process.env.VOIDDB_PASSWORD!);
const users = client.db("app").collection<User>("users");
const id = await users.insert({
name: "Alice",
age: 30,
active: true,
});
const rows = await users.find(
query()
.where("age", "gte", 18)
.where("active", "eq", true)
.orderBy("name")
.limit(25)
);
console.log(query().where("active", "eq", true).json());
console.log(rows.json());
await users.patch(id, { age: 31 });
await users.delete(id);Blob fields and file uploads
Blob is a first-class schema type. The ORM can upload a file directly into a document field:
import { VoidClient } from "@voiddb/orm";
import type { BlobRef } from "@voiddb/orm";
type Asset = {
_id: string;
title: string;
original?: BlobRef;
};
const client = VoidClient.fromEnv();
await client.login(process.env.VOIDDB_USERNAME!, process.env.VOIDDB_PASSWORD!);
const assets = client.db("media").collection<Asset>("assets");
const ref = await assets.uploadFile(
"asset-123",
"original",
new TextEncoder().encode("hello voiddb"),
{
filename: "hello.txt",
contentType: "text/plain",
}
);
console.log(ref._blob_url);
console.log(assets.blobUrl(ref));To delete the file again:
await assets.deleteFile("asset-123", "original");Zero-config project layout
Initialize a project once:
npx --package=@voiddb/orm vdb initThat creates:
.env.example
.voiddb/
config.json
schema/
app.schema
generated/
voiddb.generated.d.ts
index.d.ts
index.js
migrations/The CLI automatically reads:
.env.env.local.voiddb/.env.voiddb/config.json
By default it expects:
VOIDDB_URLVOIDDB_TOKENVOIDDB_USERNAMEVOIDDB_PASSWORD
New .schema format
The default schema format is grouped by database:
datasource db {
provider = "voiddb"
url = env("VOIDDB_URL")
}
generator client {
provider = "voiddb-client-js"
output = "../generated"
}
database {
name = "app"
model User {
id String @id
email String @unique
name String
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
@@map("users")
}
}Older top-level model ... syntax is still parsed for compatibility.
Short CLI commands
After install, the short bin is vdb.
Local project:
npm install -D @voiddb/orm
npx vdb init
npx vdb pull
npx vdb push
npx vdb gen
npx vdb dev --name add_users
npx vdb status
npx vdb deployWithout installing first:
npx --package=@voiddb/orm vdb init
npx --package=@voiddb/orm vdb pull
bunx --package @voiddb/orm vdb dev --name add_usersThe long forms still work too:
npx --package=@voiddb/orm voiddb-orm schema pull
npx --package=@voiddb/orm voiddb-orm migrate statusPull, push, and migrate
With .env in the project root, commands no longer need repeated URL or auth flags:
VOIDDB_URL=https://db.lowkey.su
VOIDDB_USERNAME=admin
VOIDDB_PASSWORD=your-passwordThen:
npx vdb pull
npx vdb push
npx vdb dev --name add_status
npx vdb statusType generation runs automatically after pull, push, dev, and deploy unless you pass --no-generate.
Schema sync is scoped to the databases explicitly present in the schema file. Databases not mentioned in the schema are not changed, dropped, or rewritten.
Generated types
Generated declarations live in:
.voiddb/generated/voiddb.generated.d.ts
.voiddb/generated/index.d.tsSo you can import from the folder instead of the generated filename:
import type {
User,
UserCreateInput,
VoidDbGeneratedCollections,
VoidDbGeneratedCollectionsByPath,
VoidDbGeneratedDatabases,
} from "./.voiddb/generated";Use the generated maps like this:
type ExactLowkeyUser = VoidDbGeneratedDatabases["lowkey"]["users"];
type AnyUsersCollection = VoidDbGeneratedCollections["users"];
type ExactPath = VoidDbGeneratedCollectionsByPath["lowkey/users"];
const users = client
.database("lowkey")
.collection<VoidDbGeneratedDatabases["lowkey"]["users"]>("users");You can also regenerate explicitly:
npx vdb genQuery builder
const built = query()
.where("age", "gte", 18)
.orderBy("createdAt", "desc")
.limit(10);
const rows = await users.find(built);
const rawQuery = built.json();
const first = rows.first();
const plainRows = rows.toArray();
const jsonRows = rows.json();You can also use a shorthand object filter for equality checks:
const admins = await users.find({
where: {
isAdmin: true,
},
});If you prefer a more explicit method name, .query() is an alias of .find():
const admins = await users.query({
where: {
isAdmin: true,
},
});Relation includes
const result = await client
.db("app")
.collection("users")
.findWithRelations<{
profile: { _id: string; bio: string };
}>(
query()
.where("_id", "eq", "user-1")
.include({
as: "profile",
relation: "many_to_one",
target_col: "profiles",
local_key: "profile_id",
foreign_key: "_id",
})
);Cache API
await client.cache.set("session:alice", { loggedIn: true }, 3600);
const session = await client.cache.get<{ loggedIn: boolean }>("session:alice");
await client.cache.delete("session:alice");Links
- npm: @voiddb/orm
- ORM docs: nopass0.github.io/void_ts
- Core server repo: github.com/Nopass0/void
- Core server docs: nopass0.github.io/void
- AI agent guide exposed by running servers:
https://<host>/skill.md
License
MIT
