npm install sencillodbSencilloDB is written in TypeScript and ships its own type definitions. It is ESM only and requires Node 18 or newer.
Use SencilloDB when your Node app wants durable local JSON data with transactions, indexes and migrations, but you do not want to run a database server. It is a strong fit for CLIs, desktop apps, prototypes, small internal tools, build caches, bots and tests.
Reach for a server database instead when you need many high-frequency writers, complex joins or analytics, replication, remote sync, database-enforced permissions, or storage on an unreliable network filesystem. The longer decision guide is Use Cases and Limits.
import { SencilloDB } from "sencillodb";
const db = new SencilloDB({ file: "./my-database.json" });
const user = await db.transaction(async (tx) => {
return tx.create({
collection: "users",
data: { name: "Alice", age: 30 },
});
});
console.log(user); // { name: "Alice", age: 30, _id: 1 }The file is created for you if it does not exist (including any missing parent folders). Every write happens inside a transaction, and nothing reaches disk until the callback returns without throwing.
const adults = await db.transaction((tx) =>
tx.findMany({
collection: "users",
filter: { age: { $gte: 18 } },
sort: (a, b) => a.age - b.age,
limit: 10,
})
);
const total = await db.transaction((tx) => tx.count({ collection: "users" }));find returns the first match (or undefined), findMany returns an array, count returns a number.
await db.transaction(async (tx) => {
// patch a few fields
await tx.update({ collection: "users", _id: 1, $set: { age: 31 } });
// replace the whole document
await tx.update({ collection: "users", _id: 1, data: { name: "Alice", age: 31 } });
// patch everything matching a filter
await tx.updateMany({ collection: "users", filter: { age: { $lt: 18 } }, $set: { minor: true } });
await tx.destroy({ collection: "users", _id: 1 });
});A secondary index turns a scan into a direct lookup, and can enforce uniqueness:
await db.transaction(async (tx) => {
await tx.ensureIndex({ collection: "users", field: "email", unique: true });
});
// uses the index
const alice = await db.transaction((tx) =>
tx.find({ collection: "users", filter: { email: "alice@example.com" } })
);| Mode | Config | Good for |
|---|---|---|
| Single file | { file: "./app.json" } |
Small stores, easy to inspect and copy |
| Folder | { folder: "./data" } |
Many collections, only touched ones load and save |
| Sharded | { folder: "./data", sharding: true } |
Large collections split across index buckets |
See Persistence Modes for the trade-offs.
Start with one file unless you already know why you need another layout:
const db = new SencilloDB({
file: "./data/app.json",
aof: true,
});That gives you the simplest file layout plus cheap append-only commits. Add lock: true only if more than one process can write to the same store. Run compact() occasionally so the append only log does not grow forever:
await db.compact();- Core Concepts — collections, index buckets, transactions
- Use Cases and Limits — where SencilloDB fits
- Querying — operators, sorting, pagination, relations
- Indexes, Unique Constraints and TTL
- API Reference
- Advanced Usage