Guides
Getting started
Connect to a Rivyn server and run your first queries.
@rivyn/db is the TypeScript client for Rivyn, a self-hosted
NoSQL database. It speaks a length-prefixed binary protocol over TCP, reconnects on its
own, and ships with zero runtime dependencies.
Install
npm i @rivyn/dbRequires Node 20.19+. The package is ESM, but Node's require(esm) support means it also
works from CommonJS projects:
import { RivynClient } from "@rivyn/db"; // ESM
const { RivynClient } = require("@rivyn/db"); // CommonJSYou also need a running @rivyn/db-server instance — on your
own machine or your VPS. There is no hosted service.
Connect
import { RivynClient } from "@rivyn/db";
const client = new RivynClient({
host: "127.0.0.1",
port: 7223,
key: process.env.RIVYN_KEY!,
});The client connects lazily on the first request, so there is nothing to await at startup.
Call client.connect() explicitly if you want to fail fast instead.
Queries
interface User {
name: string;
age: number;
[key: string]: unknown;
}
const users = client.collection<User>("users");
await users.insert({ name: "arel", age: 21 });
await users.insertMany([
{ name: "mert", age: 17 },
{ name: "zeynep", age: 25 },
]);
const adults = await users.find({ age: { $gt: 18 } }, { sort: { age: -1 }, limit: 10 });
const arel = await users.findOne({ name: "arel" });
await users.updateOne({ name: "arel" }, { $inc: { age: 1 }, $set: { active: true } });
await users.deleteMany({ age: { $lt: 18 } });Filters support $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists,
$regex, $elemMatch, $or, $and and dotted paths. Updates support $set, $unset,
$inc, $push, $pull and $addToSet, plus positional operators and upsert — see
queries and updates.
Indexes
Range queries are answered by a sorted index rather than a full scan, so declaring one matters:
await users.createIndex("age");
await users.createIndex("email", { unique: true });
await users.createIndex("punishments.active"); // multikey, indexes every elementA path that crosses an array builds a multikey index: the document is indexed under every
value the path produces, and still returned once. With a schema you declare these with
index: true instead and the model creates them on first write.
Aggregation
const byTeam = await users.aggregate([
{ $match: { active: true } },
{ $group: { _id: "$team", total: { $sum: "$score" }, avg: { $avg: "$score" } } },
{ $sort: { total: -1 } },
{ $limit: 5 },
]);Reconnection
The connection recovers on its own with exponential backoff. Subscribe if you want to observe it:
client.on("disconnect", () => console.warn("rivyn: connection lost"));
client.on("connect", (info) => console.info(`rivyn: connected to ${info.server}`));A request that is in flight when the connection drops is rejected, not buffered. That is deliberate: a buffered write looks like it succeeded, and vanishes silently if the process restarts before reconnecting. An error surfaces the problem instead.
Next: queries and updates, then schemas for validated, structured collections.