@liorandb/driver
v1.1.5
Published
TypeScript and JavaScript driver for the LioranDB Host API.
Readme
@liorandb/driver
TypeScript and JavaScript driver for the LioranDB Host API.
This package gives you a small MongoDB-style interface on top of the host server running at http://<host>:4000.
Installation
npm install @liorandb/driverBefore You Connect
The host will not start until at least one admin user exists.
Create the first admin with the CLI:
ldb-cli 'admin.create("admin","password123")'Then start the host and authenticate through /auth/login or /auth/register.
Quick Start
import { LioranClient } from "@liorandb/driver";
const client = new LioranClient("http://localhost:4000");
await client.login("admin", "password123");
const db = client.db("app");
const users = db.collection<{ name: string; age: number }>("users");
await users.insertOne({ name: "John", age: 20 });
const results = await users.find({ age: { $gt: 18 } });
console.log(results);Connection Formats
The client accepts these URI formats:
http://<host>:<port>
https://<host>:<port>
liorandb://<dbUsername>:<dbPassword>@<host>:<port>/<dbName>
lioran://<username>:<password>@<host>:<port>Examples:
http://localhost:4000
https://db.example.com:4000
liorandb://app_user:app_pass@localhost:4000/app
lioran://admin:password123@localhost:4000 (legacy)Use http(s)://... when you want to call login(), superAdminLogin(), setToken(), or setConnectionString() yourself.
Use liorandb://... when you want connect() (or setConnectionString()) to authenticate using a database connection string.
Use lioran://... when you want connect() to log in from URI credentials (legacy format).
Authentication
Login with username and password
const client = new LioranClient("http://localhost:4000");
const auth = await client.login("admin", "password123");
console.log(auth.user.username);
console.log(client.getToken());Super admin login
const client = new LioranClient("http://localhost:4000");
await client.superAdminLogin(process.env.LIORAN_SUPER_ADMIN_SECRET!);Register a user
const client = new LioranClient("http://localhost:4000");
await client.login("admin", "password123");
await client.register("editor", "password123");Connect from a lioran:// URI
const client = new LioranClient(
"lioran://admin:password123@localhost:4000"
);
await client.connect();Connect from a liorandb:// connection string
const client = new LioranClient(
"liorandb://app_user:app_pass@localhost:4000/app"
);
await client.connect();
const db = client.db("app");Reuse an existing JWT
const client = new LioranClient("http://localhost:4000");
client.setToken(process.env.LIORAN_TOKEN!);Use an existing database connection string
const client = new LioranClient("http://localhost:4000");
client.setConnectionString(process.env.LIORANDB_CONNECTION_STRING!);Protected driver methods throw until the client is authenticated (via login(), superAdminLogin(), connect(), setToken(), or setConnectionString()).
Client API
Constructor
const client = new LioranClient(uri);connect()
Supports:
liorandb://...: stores the connection string and authenticates viax-liorandb-connection-stringlioran://...: logs in via/auth/login(legacy format)http(s)://user:pass@host:port: logs in via/auth/login
await client.connect();login(username, password)
const auth = await client.login("admin", "password123");Returns:
{
user: { userId: string, username: string, role: "super_admin" | "admin" | "user" },
token: string
}superAdminLogin(secret)
const auth = await client.superAdminLogin(process.env.LIORAN_SUPER_ADMIN_SECRET!);Returns the same shape as login().
register(username, password)
await client.login("admin", "password123");
await client.register("editor", "password123");Requires authentication and returns the same shape as login().
Note: if you call register(input) without a password, the server returns token: null.
register(input)
Create a user with more control (user id, role, external id).
await client.login("admin", "password123");
await client.register({
userId: "editor_1",
username: "editor",
password: "password123",
role: "user",
externalUserId: "auth0|abc123"
});me()
Calls GET /auth/me and updates client.getUser().
const me = await client.me();
console.log(me.user.role);listUsers()
Calls GET /auth/users.
const users = await client.listUsers();updateMyCors(origins)
Calls PUT /auth/me/cors.
await client.updateMyCors(["https://app.example.com"]);updateUserCors(userId, origins)
Calls PUT /auth/users/:userId/cors (admin-only).
await client.updateUserCors("user_123", ["https://app.example.com"]);issueUserToken(userId)
Calls POST /auth/users/:userId/token.
const { token } = await client.issueUserToken("editor_1");health()
Calls GET /health.
const health = await client.health();Response:
{
ok: true,
time: "2026-04-13T12:00:00.000Z"
}info()
Calls GET /.
const info = await client.info();Response:
{
name: "LioranDB",
role: "Database Host",
status: "online"
}listDocs() / getDoc(id)
Calls GET /docs and GET /docs/:id.
const { docs } = await client.listDocs();
const apiDoc = await client.getDoc("api");maintenanceStatus() / listSnapshots() / createSnapshotNow()
Calls admin-only maintenance endpoints under /maintenance/*.
await client.maintenanceStatus();
await client.listSnapshots();
await client.createSnapshotNow();setToken(token)
client.setToken(jwt);setConnectionString(connectionString)
client.setConnectionString("liorandb://user:pass@localhost:4000/app");getToken()
const token = client.getToken();getConnectionString()
const cs = client.getConnectionString();getUser()
Returns the last authenticated user from login(), superAdminLogin(), me(), or connect().
const user = client.getUser();isAuthenticated()
if (client.isAuthenticated()) {
// ...
}logout()
Clears the in-memory JWT/connection-string auth state and current user.
client.logout();db(name)
const db = client.db("app");listDatabases()
Calls GET /databases.
const databases = await client.listDatabases();countDatabases(userId?)
Calls GET /databases/count (optionally filtered by userId).
const { count } = await client.countDatabases();listUserDatabases(userId)
Calls GET /databases/user/:userId.
const res = await client.listUserDatabases("editor_1");
console.log(res.count, res.databases);createDatabase(name)
Calls POST /databases.
const res = await client.createDatabase("app");Response:
{ ok: true, database: { databaseName: "app", ... } }createDatabase({ name, ownerUserId? })
const res = await client.createDatabase({ name: "app", ownerUserId: "editor_1" });dropDatabase(name)
Calls DELETE /databases/:db.
const res = await client.dropDatabase("app");Response:
{ ok: true }databaseStats(name)
Calls GET /databases/:db/stats.
const stats = await client.databaseStats("app");Response:
{
name: "app",
collections: 2,
documents: 154
}DB API
const db = client.db("app");collection(name)
const users = db.collection("users");You can pass a generic type for better autocomplete:
type User = {
name: string;
age: number;
active?: boolean;
};
const users = db.collection<User>("users");listCollections()
Calls GET /db/:db/collections.
const collections = await db.listCollections();createCollection(name)
Calls POST /db/:db/collections.
const res = await db.createCollection("users");Response:
{ ok: true, collection: "users" }dropCollection(name)
Calls DELETE /db/:db/collections/:col.
const res = await db.dropCollection("users");renameCollection(oldName, newName)
Calls PATCH /db/:db/collections/:col/rename.
const res = await db.renameCollection("users", "customers");stats()
Calls GET /databases/:db/stats.
const stats = await db.stats();getCredentials()
Calls GET /databases/:db/credentials.
const creds = await db.getCredentials();
console.log(creds.username, creds.password);setCredentials({ username, password })
Calls PUT /databases/:db/credentials.
await db.setCredentials({ username: "app_user", password: "app_pass" });getConnectionString()
Calls GET /databases/:db/connection-string.
const { connectionString } = await db.getConnectionString();Collection API
const users = db.collection<User>("users");insertOne(doc)
Calls POST /db/:db/collections/:col.
const user = await users.insertOne({ name: "John", age: 20 });insertMany(docs)
Calls POST /db/:db/collections/:col/bulk.
const inserted = await users.insertMany([
{ name: "A", age: 20 },
{ name: "B", age: 21 }
]);find(filter, options?)
Calls POST /db/:db/collections/:col/find.
const adults = await users.find({ age: { $gt: 18 } });options supports (server passes through to the underlying query engine):
limit?: numberoffset?: numberprojection?: string[]sort?: Record<string, 1 | -1>
findOne(filter, options?)
const john = await users.findOne({ name: "John" });Returns the first matching document or null.
updateMany(filter, update)
Calls PATCH /db/:db/collections/:col/updateMany.
const res = await users.updateMany(
{ age: { $lt: 18 } },
{ $set: { minor: true } }
);Response:
{ updated: 3, docs: [] }aggregate(pipeline)
Calls POST /db/:db/collections/:col/aggregate.
const res = await users.aggregate([
{ $match: { age: { $gte: 18 } } },
{ $limit: 10 }
]);explain(query, options?)
Calls POST /db/:db/collections/:col/explain.
const plan = await users.explain({ age: { $gte: 18 } }, { limit: 50 });deleteMany(filter)
Calls POST /db/:db/collections/:col/deleteMany.
const res = await users.deleteMany({ inactive: true });Response:
{ deleted: 5 }count(filter)
Calls POST /db/:db/collections/:col/count.
const total = await users.count({ active: true });stats()
Calls GET /db/:db/collections/:col/stats.
const stats = await users.stats();Response:
{
name: "users",
documents: 42
}Error Handling
The driver throws regular Error values for client-side validation issues and HttpError for failed HTTP requests.
import { HttpError, LioranClient } from "@liorandb/driver";
try {
await client.login("admin", "wrong-password");
} catch (error) {
if (error instanceof HttpError) {
console.error(error.status);
console.error(error.data);
}
}Exports
import {
Collection,
DB,
HttpClient,
HttpError,
LioranClient
} from "@liorandb/driver";The package also exports the shared TypeScript types from src/types.ts (for example: Filter, UpdateQuery, and response types).
License
MIT
