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

narise

v1.0.1

Published

In memory database for Node.js

Readme

Narise

A lightweight, schema-driven in-memory database for Node.js. Zero dependencies, pure JavaScript (ESM), with JSON persistence.

Narise keeps your data in memory for fast reads while persisting it to plain JSON files with automatic saving and backup. Define a typed schema — an order of tables — and Narise enforces types, constraints, defaults, and uniqueness, automatically correcting or rejecting invalid values.

Features

  • Schema-driven tables — describe columns, types, constraints, and defaults in a single declarative order object.
  • Auto value correction — every value is validated and corrected against its column definition before being stored.
  • Rich column typesstring, number, boolean, enum, object, array, and time.
  • Multi-key indices — primary keys, single & composite unique keys, and common (secondary) keys are indexed in memory for fast lookups.
  • Auto-generated IDs — single-column primary keys are filled automatically; every row also gets an auto-incrementing $id.
  • Powerful filtering — comparison operators ($eq, $gt, $in, $regex, …), boolean logic ($and, $or, $nor, $not), and keyword matching ($text).
  • Joinsinner and left joins on matching keys or custom functions.
  • Fluent CRUDinsert, select, update, upsert, and remove through a chainable query API.
  • JSON persistence — one file per table; incremental saving only rewrites dirty tables.
  • Auto save & backup — configurable save/backup intervals, plus on-demand backups.
  • Schema repair — migrate existing data to a new schema, correcting fields and supplying defaults.
  • Zero dependencies — built on Node.js built-ins only.

Installation

npm install narise

Requires Node.js with ES module support (the package uses "type": "module").

Quick Start

import narise from "narise";

// 1. Define an order of tables (the schema).
const order = {
    user: {
        primary: ["userId"],
        unique: ["username"],
        common: ["status"],
        columns: {
            userId:     { type: "string", restrict: 12 },
            username:   { type: "string", restrict: 32 },
            email:      { type: "string", restrict: 128, default: "" },
            status:     { type: "enum", restrict: ["active", "inactive", "banned"], default: "active" },
            profile:    { type: "object", restrict: null, default: {} },
            score:      { type: "number", restrict: { integerDigits: 12, fractionDigits: 2 }, default: 0 },
            verified:   { type: "boolean", restrict: null, default: false },
            createTime: { type: "time", restrict: null },
            updateTime: { type: "time", restrict: null }
        }
    }
};

// 2. Deploy the database.
const db = narise({ name: "app", path: "./data" });
await db.deploy(order, { autoSaveInterval: 5000 });

// 3. Insert rows.
const users = db.table("user").insert([
    { username: "alice", score: 100, createTime: Date.now(), updateTime: Date.now() },
    { username: "bob", score: 50, createTime: Date.now(), updateTime: Date.now() }
]).exec();

// 4. Query with filters, sorting, and limits.
const result = db.table("user")
    .where({ status: "active", score: { $gte: 50 } })
    .sort([["score", 1]])        // a positive value means descending
    .limit(10)
    .select()
    .exec();

// 5. Update, upsert, remove.
db.table("user").where({ username: "alice" }).update({ score: 120 }).exec();
db.table("user").upsert({ userId: users[0].userId, score: 130 }).exec();
db.table("user").where({ username: "bob" }).remove().exec();

// 6. Close (saves everything).
await db.close();

Table Order (Schema)

A database is defined by an order — an object whose keys are table names and whose values describe each table.

{
    "tableName": {
        "primary": ["id"],              // array of primary key column(s)
        "unique":  ["username"],        // array of unique column(s); nested arrays for composite keys
        "common":  ["status"],          // array of secondary indexed column(s)
        "columns": {
            "columnName": {
                "type": "string",       // see Column Types
                "restrict": 32,         // type-specific constraint (see below)
                "default": ""           // optional default value
            }
        }
    }
}
  • primary (required, non-empty) — a single-column primary is auto-filled with a random 12-character ID; a composite primary is also indexed as common columns.
  • unique (optional) — prevents duplicates. Composite unique keys are declared as nested arrays, e.g. [["name", "scope"]].
  • common (optional) — secondary columns indexed for fast equality filtering.
  • columns — column definitions. Every value is corrected against its definition (type + restrict), or supplied with the default when missing.

Column Types

| Type | Description | restrict | | --- | --- | --- | | string | Text value | maximum length (a number); longer values are truncated | | number | Numeric value | { integerDigits, fractionDigits }; out-of-range values are rejected | | boolean | true / false | null | | enum | One of a fixed set of values | array of allowed values | | object | Any plain object | null | | array | Any array | null | | time | A timestamp (millisecond number) | null |

Columns without a default are required — inserting a row that omits them throws an error.

Database API

narise(options) returns a Database instance.

| Method | Description | | --- | --- | | exists() | Whether the database has been deployed. | | deploy(order, options) | Create and deploy a new database from an order. | | start(options) | Open an existing database; optionally autoRepair with repairOrder / repairOptions. | | repair(newOrder, options) | Migrate existing table data to a new schema (optional backup). | | close() | Clear timers and save everything. | | addTable(name, detail, persistent = true) | Add a table at runtime. | | getTable(name) | Get the raw Table object. | | orderTable(name, detail) | Add a table definition to the order. | | clearTable(name) | Remove all rows from a table. | | deleteTable(name) | Remove a table and its file. | | table(name) | Get a chainable Query object for the table. |

Boot options (for deploy / start):

  • autoSaveInterval — auto-save interval in ms (default 5000; 0 disables).
  • autoBackupInterval — auto-backup interval in ms (default 0, disabled).

Query API

Every query is fluent and chainable; call exec() to run it.

| Method | Description | | --- | --- | | where(method) | Filter rows using a function or a filter object. | | join(method) | Join with another table (inner / left). | | sort(method) | Sort by [[column, dir], …]. | | offset(index) | Skip the first N rows. | | limit(count) | Keep at most N rows. | | select() | Return the resulting rows. | | insert(values) | Insert a row or an array of rows. | | update(values) | Update the filtered rows. | | upsert(values) | Insert or update by primary key. | | remove() | Remove the filtered rows. | | count() | Number of currently matched rows. | | exec(process) | Execute and return the result; process may transform an array result. |

Filter Operators

Simple equality works directly ({ status: "active" }). For more, use operator objects:

| Operator | Description | | --- | --- | | $eq / $ne | Equal / not equal | | $gt / $gte / $lt / $lte | Comparison | | $in / $nin | In / not in an array | | $ins / $nins | In / not in a Set | | $regex | RegExp match | | $exists | Column presence | | $and / $or / $nor | Combine sub-filters | | $not | Negate a sub-filter | | $text | Keyword matching, e.g. { $text: { columns: ["title"], keywords: ["hello"] } } |

You can also pass a plain function to where() for full control.

Joins

db.table("post")
    .where({ status: "published" })
    .join({
        table: "user",             // join target table
        on: ["userId", "userId"],  // [leftKey, rightKey], or a function (leftRow, rightRow)
        as: "author",              // property name on the result (defaults to the table name)
        where: { status: "active" }, // optional pre-filter on the join target
        type: "left"               // "left" (default) or "inner"
    })
    .select()
    .exec();

Sorting

.sort([["score", 1], ["name", -1]])

Note: a positive value means descending; a negative or zero value means ascending.

Persistence & Backup

Deploying a database named app in ./data produces:

data/
├── app.json            # main file: { config, order }
├── app.user.json       # table data (one file per table)
└── backups/            # timestamped backups (when enabled)
  • Incremental save — only tables marked dirty are rewritten.
  • BackupsaveBackup() or autoBackupInterval writes the whole database into a timestamped folder.
  • Repairrepair(newOrder, { backup: true }) backs up the current tables before migrating, then corrects every row against the new schema — fixing invalid values, supplying defaults for new columns, and dropping removed columns.

Running the Self-Test

index.js ships a self-contained demo (Reviser) plus a self-test that deploys a small user / post / tag database and exercises insert, filter, sort, join, update, upsert, and remove:

npm start        # or: node index.js

You should see Self-test passed. — the temporary .demo folder is cleaned up automatically.

License

MIT