npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

flxdb

v2.0.0

Published

A lightweight, fast and persistent key-value database for Node.js with namespaces, TTL, JSON schema validation, transactions, watch/unwatch, backup/restore and EventEmitter support.

Downloads

137

Readme

📁 flxdb

flxdb is a lightweight, fast, file-based key-value (KV) database designed for modern Node.js applications.

✔ Zero-configuration
✔ Persistent JSON storage
✔ Dot-notation key paths
✔ Namespaces, TTL & schema validation
✔ Transactions with automatic rollback
✔ EventEmitter — react to any change
✔ Debounced writes & async save
✔ Full TypeScript support


🆕 What's new in v2.0.0

Fully backward-compatible. Your existing set/get code continues to work without any changes.

| Category | New | |---|---| | ⚡ Performance | Debounced save, dirty flag, TTL min-heap, async write mode | | 🔔 Events | EventEmitterset, delete, expire, clear, save, backup, restore, transaction:commit/rollback | | 🔁 Transactions | transaction(fn) — atomic ops with auto rollback on error | | 👁 Watch | watch(key, fn) / unwatch(key, fn) — react to key changes | | ⏱ TTL | expire(key, ms) — assign TTL to existing keys; ttl(key) — check remaining time | | 🗃 Bulk ops | bulkSet([[k,v], ...]) — multiple writes in one disk flush | | 🔢 Math | increment(key) / decrement(key) aliases | | 🧺 Arrays | Variadic push(key, ...values), pullWhere(key, fn) | | 🔍 Query | filter(fn), find(fn), map(fn) over all entries | | 🛠 Utilities | rename(old, new), copy(src, dst), size() | | 💾 Backup | backup(path) / restore(path, overwrite?) | | 📐 Schema | min, max, enum, match (RegExp) constraints added | | 🏗 Custom instances | require("flxdb").FlxDBCore for multiple DB files |


📦 Installation

npm install flxdb

🚀 Quick Example

const db = require("flxdb");

db.set("user.name", "Lewira");
db.add("system.uptime", 1);

console.log(db.get("user.name")); // "Lewira"
console.log(db.all());            // full database object

🧩 API Reference

⭐ Core Methods

set(key, value, options?)

db.set("app.theme", "dark");
db.set("user.profile.age", 24);

// With TTL
db.set("cache.token", "abc123", { ttl: 5_000 }); // expires in 5s

// With schema validation
db.set("users.1", { id: "1", xp: 100 }, { schema: "userProfile" });

set(object) — deep merge

db.set({
  app: { version: "2.0.0" },
  cache: { enabled: true }
});

bulkSet(entries)

Write multiple key-value pairs in a single disk flush.

db.bulkSet([
  ["server.port", 3000],
  ["server.host", "localhost"],
  ["cache.ttl",   60_000, { ttl: 60_000 }],
]);

get(key, defaultValue?)

db.get("app.version", "0.0.0");

If a key has expired due to TTL, get returns the default value.

fetch(key, defaultValue?) — alias of get()

has(key)

db.has("cache.enabled"); // false if expired

ensure(key, defaultValue)

const port = db.ensure("server.port", 8080);

delete(key)

db.delete("user.session");

deleteAll() — alias of clear()


🔢 Numeric Operations

db.add("metrics.requests", 1);
db.subtract("metrics.tasks", 2);
db.increment("stats.hits");     // +1
db.decrement("stats.errors");   // -1

🧺 Array Operations

// Push one or more values
db.push("logs", { type: "start", at: Date.now() });
db.push("tags", "v2", "stable", "latest");

// Remove by value
db.pull("tags", "deprecated");

// Remove by condition
db.pullWhere("logs", entry => entry.type === "debug");

📚 Data & Key Listing

db.all();              // deep-cloned object of entire DB
db.allArray();         // [{ key, value }, ...]
db.keys();             // ["user.name", "server.port", ...]
db.keys("user.");      // keys starting with "user."
db.startsWith("cache."); // [{ key, value }, ...] with prefix
db.size();             // total leaf key count

🔍 Query Helpers

// Filter entries by value
db.filter(v => typeof v === "number" && v > 100);
// → [{ key: "stats.views", value: 420 }, ...]

// Find the first matching entry
db.find(v => v?.role === "admin");
// → { key: "users.7.role", value: "admin" }

// Transform all entries
db.map((v, k) => `${k} = ${v}`);

🧪 Type Checking

db.type("user.profile");  // "object"
db.type("tags");          // "array"
db.type("missing");       // "undefined"

🛠 Utilities

rename(oldKey, newKey)

Renames a key, preserving its value and TTL metadata.

db.rename("user.tmp", "user.profile");

copy(srcKey, dstKey)

Deep-clones a key's value to a new key.

db.copy("defaults.config", "users.123.config");

🧱 Namespaces

Namespaces let you organize data into logical sections inside the same file.

const guilds = db.namespace("guilds");
const users  = db.namespace("users");

guilds.set("123.premium", true);
guilds.set("123.settings.prefix", "!");
users.set("456.profile", { xp: 100, level: 3 });

guilds.get("123.premium");       // true
users.get("456.profile.level");  // 3

guilds.all();       // nested object of this namespace
guilds.allArray();  // [{ key, value }, ...]
guilds.keys();      // inner keys only
guilds.size();      // entry count
guilds.filter(v => v === true);
guilds.find(v => v?.premium);
guilds.map((v, k) => k);

Internally guilds.set("123.premium", true) becomes db.set("guilds.123.premium", true).


⏱ TTL (Time-To-Live)

Inline TTL via set()

db.set("session.token", "xyz", { ttl: 30_000 }); // 30 seconds

expire(key, ms) — assign TTL to an existing key

db.set("user.tempBan", true);
db.expire("user.tempBan", 60_000); // ban lifts after 60s

ttl(key) — remaining time in ms

db.ttl("session.token"); // e.g. 22450
db.ttl("user.name");     // -1  (no TTL = lives forever)
db.ttl("missing");       // null (key does not exist)

After expiration:

  • get(key) → returns default value
  • has(key)false
  • Key is automatically removed
  • The expire event is emitted

TTL metadata is kept in memory only — never written to the JSON file.


📏 JSON Schema Validation

db.registerSchema("userProfile", {
  type: "object",
  required: ["id", "xp"],
  props: {
    id:   { type: "string" },
    xp:   { type: "number", min: 0, max: 99999 },
    role: { type: "string", enum: ["admin", "member", "guest"] },
    tag:  { type: "string", match: /^[a-z]+$/ },
  },
});

// ✅ Valid
db.set("users.1", { id: "1", xp: 500, role: "admin" }, { schema: "userProfile" });

// ❌ Throws — xp is a string
db.set("users.2", { id: "2", xp: "abc" }, { schema: "userProfile" });

// ❌ Throws — role not in enum
db.set("users.3", { id: "3", xp: 0, role: "superuser" }, { schema: "userProfile" });

Available prop constraints: type, min, max, enum, match


🔁 Transactions

All operations inside transaction() are applied atomically. If anything throws, the entire database is rolled back to its previous state.

db.transaction(tx => {
  const balance = tx.get("wallet.balance", 0);
  if (balance < 50) throw new Error("Insufficient funds");

  tx.set("wallet.balance", balance - 50);
  tx.set("wallet.spent", tx.get("wallet.spent", 0) + 50);
});

On error the transaction:rollback event is emitted and the error is re-thrown.


🔔 EventEmitter

flxdb extends Node's EventEmitter. You can react to any database event.

db.on("set",    (key, newVal, oldVal) => console.log(`${key} changed`));
db.on("delete", (key, oldVal)         => console.log(`${key} deleted`));
db.on("expire", (key, oldVal)         => console.log(`${key} expired`));
db.on("clear",  ()                    => console.log("DB cleared"));
db.on("save",   ()                    => console.log("Flushed to disk"));

db.on("transaction:commit",   ()    => console.log("Transaction committed"));
db.on("transaction:rollback", (err) => console.error("Rolled back:", err));

db.on("backup",  (path) => console.log("Backed up to", path));
db.on("restore", (path) => console.log("Restored from", path));

👁 Watch

Subscribe to changes on a specific key.

const off = db.watch("user.xp", (newVal, oldVal) => {
  console.log(`XP changed: ${oldVal} → ${newVal}`);
});

db.set("user.xp", 500); // triggers the watcher

off(); // stop watching

💾 Backup & Restore

// Save a snapshot
db.backup("./backups/2024-01-01.json");

// Restore — deep-merge into current data
db.restore("./backups/2024-01-01.json");

// Restore — overwrite all existing data
db.restore("./backups/2024-01-01.json", true);

⚙ Advanced Options

Create a custom instance with FlxDBCore for full control (e.g. multiple database files):

const { FlxDBCore } = require("flxdb");

const db = new FlxDBCore({
  filePath:     "./data/mydb.json", // custom file path
  autosave:     true,               // write on every change (default: true)
  indent:       null,               // null = minified JSON
  debounceMs:   200,                // batch writes within 200ms window
  asyncSave:    true,               // non-blocking fs.writeFile
  ttlIntervalMs: 30_000,            // TTL sweep every 30s
});

forceSave()

Bypass the debounce and flush to disk immediately.

db.forceSave();

destroy()

Stop internal timers and perform a final save. Call before process exit.

process.on("exit", () => db.destroy());

🗄 Storage Behavior

  • All data stored in ./flxdb/flxdb.json by default
  • Writes are debounced (default 100 ms) — multiple rapid changes cost one disk write
  • Dirty flag prevents unnecessary writes when data hasn't changed
  • Dot-notation creates deeply nested JSON structure
  • Deep object merge on set(object)
  • TTL metadata is in-memory only — never written to the JSON file
  • Namespaces work via key prefixes — no extra files

🛠 Suitable For

  • App & server configuration
  • CLI tool persistence
  • Local JSON storage
  • Lightweight caching with TTL
  • Metrics & counters
  • Discord bot data (guild/user/settings)
  • Small & medium Node.js apps needing a simple KV store

🧪 Example: Structured Config

db.set({
  server: { port: 3000, secure: false },
  app:    { mode: "production", version: "2.0.0" }
});

db.get("server.port"); // 3000
db.get("app.mode");    // "production"

⭐ Support

If you like the project, feel free to leave a ⭐!
Contributions and suggestions are always welcome.


📄 License

MIT License