Skip to content
approval@dev

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/db

Requires 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"); // CommonJS

You 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 element

A 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.