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

davidb

v1.0.4

Published

Lightweight, MongoDB-like JSON database for Node.js

Readme

daviDB

A lightweight, MongoDB-like JSON database for Node.js and browser JS (with optional HTTP/REST API mode).

Key Features

  • Parallel operations with per-collection locking
  • Intelligent LRU caching — no eviction of actively used collections
  • Atomic updates with automatic persistence
  • MongoDB-like query syntax$gt, $in, $or, $regex, etc.
  • Batch operations for insert, update, and delete
  • Real-time events — subscribe to database changes
  • Lightweight — zero external dependencies
  • Debug mode for easy troubleshooting

Table of Contents

Introduction

daviDB is a fast, lightweight JSON database inspired by MongoDB. It supports two operation modes:

  • Local Mode — Store data in JSON files on your filesystem (Node.js only)
  • HTTP Mode — Connect to a remote daviDB server using an HTTP client

Note: Local mode is only available in Node.js. Browser environments require HTTP mode with a token.

HTTPAdapter is currently unavailable. HTTP mode (remote server connections, browser usage) is not functional at this time. Only Local Mode is supported in the current version.

Installation

NPM

npm install daviDB

Yarn

yarn add daviDB

Basic Usage

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .findById(1)
    .then((user) => {
        if (!user) return console.error('User not found');
        console.log('User found: ', user);
    })
    .catch(e => console.error('Error while finding user: ', e));

Configuration

Local Mode (Node.js)

const db = new daviDB({
  storagePath: './storage',         // Storage directory
  autoSaveInterval: 5000,           // Auto-save every 5s
  debug: true                       // Enable detailed logs
});

Local Mode Options

| Option | Type | Default | Description | |---|---|---|---| | debug | boolean | true | Enable detailed logging | | enableEvents | boolean | true | Enable events | | storagePath | string | './storage' | Directory for JSON files (Local mode) | | autoSaveInterval | number | 5000 | Auto-save interval in milliseconds | | maxDocumentsReturn | number | 100 | Max documents to return when using find/update methods | | maxDocumentsInsert | number | 100 | Max documents to insert when using insertMany method | | maxEventDocuments | number | 100 | Max documents to include in events | | maxCollectionsCacheSize | number | 20 | Max collections in cache | | autoShutdown | boolean | true | Auto-shutdown (flush & close) when received SIGINT signal |

HTTP Mode (Client)

HTTPAdapter is currently unavailable. The configuration below is documented for reference but is not functional in the current version.

const db = new daviDB({
  url: 'http://127.0.0.1:8050',
  token: 'your-auth-token',       // Required for HTTP mode
  poolSize: 10,                   // Connection pool size
  timeout: 15000,                 // Request timeout (ms)
  debug: true
});

HTTP Mode Options

| Option | Type | Default | Description | |---|---|---|---| | debug | boolean | false | Enable detailed logging | | url | string | - | Remote database URL (HTTP mode) | | token | string | - | Authentication token (HTTP mode) | | poolSize | number | 10 | HTTP connection pool size | | timeout | number | 15000 | Request timeout in milliseconds |

Insert Documents

Add new documents to a collection. Supports single or bulk insertion with automatic ID generation.

Documents automatically receive id, createdAt, and updatedAt fields. If an id is provided, daviDB will use it. Otherwise, it will generate a random one.

Insert Single Document

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .insertOne({
        id: 1,
        username: 'alice',
        email: '[email protected]',
        age: 28
    })
    .then((user) => {
        if (!user) return console.error('User with this id already exists');
        console.log('Inserted user: ', user);
    })
    .catch(e => console.error('Error while inserting user: ', e));

insertOne() returns null on ID duplication.

Insert Multiple Documents

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .insertMany([
        { id: '2', name: 'Bob', email: '[email protected]', age: 25 },
        { id: '3', name: 'Charlie', email: '[email protected]', age: 35 },
        { id: '4', name: 'Homer', email: '[email protected]', age: 45 },
        { id: '5', name: 'Marge', email: '[email protected]', age: 43 },
        { id: '6', name: 'Lisa', email: '[email protected]', age: 15 },
        { id: '7', name: 'Bart', email: '[email protected]', age: 13 },
        { id: '8', name: 'Maggie', email: '[email protected]', age: 1 }
    ], { returnDocuments: true })
    .then((result) => {
        if (result.skippedCount > 0) return console.error(`${result.skippedCount} users with these ids already exists`);
        console.log('Inserted users: ', result.insertedDocuments);
    })
    .catch(e => console.error('Error while inserting users: ', e));

| Option | Type | Default | Description | |---|---|---|---| | returnDocuments | boolean | false | Return inserted documents into insertedDocuments |

Always check skippedCount to know how many documents were skipped due to ID conflicts.

Find Documents

Query documents with filters, sorting, and pagination.

Find by ID

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .findById(1)
    .then((user) => {
        if (!user) return console.error('User not found');
        console.log('User found: ', user);
    })
    .catch(err => console.error('Error while finding user: ', err));

findById() returns null if document not found.

Find One Document

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .findOne({ email: '[email protected]' })
    .then((user) => {
        if (!user) return console.error('User not found');
        console.log('User found: ', user);
    })
    .catch(e => console.error('Error while finding user: ', e));

findOne() returns null if document not found.

Find All Documents

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .find({})
    .then(({ documents: users, pagination }) => {
        console.log('All users: ', users);
    })
    .catch(e => console.error('Error while finding users: ', e));

find() always returns { documents: [], pagination: {} }.

Find with Filters

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .find(
        { age: { $gte: 24 } },
        {
            sort: { age: -1 },
            limit: 2,
            fields: { id: 1, username: 1 }
        })
    .then(({ documents: users, pagination }) => {
        console.log('All users: ', users);
    })
    .catch(e => console.error('Error while finding users: ', e));

| Option | Type | Methods | Description | |---|---|---|---| | fields | object | findById, findOne, find | Field projection (1 include, -1 exclude) | | sort | object | findOne, find | Sort results (1 ascending, -1 descending) | | limit | number | find | Limit returned documents | | skip | number | find | Number of documents to skip | | page | number | find | Page number (alternative to skip) |

Update Documents

Modify existing documents using update operators.

Update by ID

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .updateById(1, { $inc: { age: 1 } })
    .then((updatedUser) => {
        if (!updatedUser) return console.error('User not found');
        console.log('Updated user: ', updatedUser);
    })
    .catch(e => console.error('Error while updating user: ', e));

updateById() returns null if document not found.

Update One Document

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .updateOne({ email: '[email protected]' }, { lastLogin: Date.now() })
    .then((updatedUser) => {
        if (!updatedUser) return console.error('User not found');
        console.log('Updated user: ', updatedUser);
    })
    .catch(e => console.error('Error while updating user: ', e));

updateOne() returns null if no document matched the filters.

Update Multiple Documents

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .updateMany({ age: { $gte: 18 } }, { major: true }, { returnDocuments: true })
    .then((result) => {
        if (result.updatedCount === 0) return console.error('No users found to update');
        console.log('Updated users: ', result.updatedDocuments);
    })
    .catch(e => console.error('Error while updating users: ', e));

| Option | Type | Default | Description | |---|---|---|---| | returnDocuments | boolean | false | Return updated documents into updatedDocuments |

Check updatedCount to know how many documents were updated.

Update Operators

const users = db.collection('users');

// $set - Set a value
await users.updateOne({ email: '[email protected]' }, { $set: { verified: true } });
// or simply with
await users.updateOne({ email: '[email protected]' }, { verified: true });

// $inc - Increment
await users.updateOne({ email: '[email protected]' }, { $inc: { visits: 1 } });

// $push - Add to array
await users.updateOne({ email: '[email protected]' }, { $push: { tags: 'premium' } });

// $addToSet - Add without duplicates
await users.updateOne({ email: '[email protected]' }, { $addToSet: { roles: 'admin' } });

// $pull - Remove from array
await users.updateOne({ email: '[email protected]' }, { $pull: { tags: 'banned' } });

// $unset - Delete field
await users.updateOne({ email: '[email protected]' }, { $unset: { tempField: 1 } });

Update Nested Fields

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .updateOne({ age: { $gte: 18 } }, { 'settings.language': 'en' })
    .then((user) => {
        if (!user) return console.error('User not found');
        console.log('Updated user: ', user);
    })
    .catch(err => console.error('Error while updating user: ', err));

Count Documents

Get the number of documents matching a filter.

Count All Documents

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .count({})
    .then((usersCount) => {
        console.log('Users count: ', usersCount);
    })
    .catch(e => console.error('Error while counting users: ', e));

Count with Filters

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .count({ age: { $gte: 28 } })
    .then((usersCount) => {
        console.log('Users count: ', usersCount);
    })
    .catch(e => console.error('Error while counting users: ', e));

Delete Documents

Remove documents from collections.

Delete by ID

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .deleteById(2)
    .then((user) => {
        if (!user) return console.error('User not found');
        console.log('User with ID 2 successfully deleted.');
    })
    .catch(e => console.error('Error while deleting user: ', e));

deleteById() returns false if document not found by ID.

Delete One Document

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .deleteOne({ email: '[email protected]' })
    .then((deletedId) => {
        if (!deletedId) return console.error('User not found');
        console.log('User with email "[email protected]" successfully deleted. Deleted ID :', deletedId);
    })
    .catch(e => console.error('Error while deleting user: ', e));

deleteOne() returns null if no document matched the filters.

Delete Multiple by IDs

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .deleteMany({ id: { $in: [1, 5, 8] } })
    .then((result) => {
        if (result.deletedCount === 0) return console.error('No users found to delete');
        if (result.deletedCount === 3) console.log(`All ${result.deletedCount} users deleted successfully`);
        else console.error(`Only ${result.deletedCount} users were deleted`);
    })
    .catch(e => console.error('Error while deleting users: ', e));

Check deletedCount to know how many documents were deleted.

Delete with Filters

const db = new (require('davidb'))({ debug: true });

db.collection('users')
    .deleteMany({ age: { $gte: 26 } })
    .then((result) => {
        if (result.deletedCount > 0) console.log(`Deleted ${result.deletedCount} users`);
        else console.error('No users found to delete');
    })
    .catch(e => console.error('Error while deleting users: ', e));

Filters & Operators

daviDB supports MongoDB-like query operators for filtering documents.

Complete Operators Reference

| Operator | Category | Description | Example | |---|---|---|---| | $eq | Comparison | Matches values that are equal to a specified value | { age: { $eq: 25 } } | | $ne | Comparison | Matches values that are not equal to a specified value | { status: { $ne: 'banned' } } | | $gt | Comparison | Matches values that are greater than a specified value | { age: { $gt: 18 } } | | $gte | Comparison | Matches values that are greater than or equal to a specified value | { age: { $gte: 18 } } | | $lt | Comparison | Matches values that are less than a specified value | { age: { $lt: 65 } } | | $lte | Comparison | Matches values that are less than or equal to a specified value | { age: { $lte: 65 } } | | $in | Comparison | Matches any of the values specified in an array | { status: { $in: ['active', 'pending'] } } | | $nin | Comparison | Matches none of the values specified in an array | { role: { $nin: ['admin', 'mod'] } } | | $and | Logical | Joins query clauses with a logical AND | { $and: [{ age: { $gte: 18 } }, { status: 'active' }] } | | $or | Logical | Joins query clauses with a logical OR | { $or: [{ role: 'admin' }, { role: 'mod' }] } | | $not | Logical | Inverts the effect of a query expression | { age: { $not: { $lt: 18 } } } | | $nor | Logical | Joins query clauses with a logical NOR | { $nor: [{ banned: true }, { deleted: true }] } | | $exists | Element | Matches documents that have the specified field | { email: { $exists: true } } | | $regex | Evaluation | Matches values that match a specified regular expression | { email: { $regex: /@gmail\.com$/ } } | | $mod | Evaluation | Performs a modulo operation on the value of a field | { age: { $mod: [2, 0] } } |

Comparison Operators

const users = db.collection('users');

// $eq - Equal
await users.find({ age: 25 });
await users.find({ age: { $eq: 25 } });

// $ne - Not equal
await users.find({ status: { $ne: 'banned' } });

// $gt, $gte, $lt, $lte - Greater/Less than
await users.find({ age: { $gte: 18, $lt: 65 } });

// $in - In array
await users.find({ status: { $in: ['active', 'pending'] } });

// $nin - Not in array
await users.find({ role: { $nin: ['admin', 'moderator'] } });

Logical Operators

By default, multiple filter conditions separated by commas act as a logical AND. You only need $and for complex nested queries.

// Implicit $and - Multiple conditions (recommended)
await users.find({ age: { $gte: 18 }, status: 'active' });

// Explicit $and - Same result, more verbose
await users.find({
  $and: [
    { age: { $gte: 18 } },
    { status: 'active' }
  ]
});

// $or - At least one condition must match
await users.find({
  $or: [
    { role: 'admin' },
    { role: 'moderator' }
  ]
});

// $not - Negation
await users.find({ age: { $not: { $lt: 18 } } });

// $nor - None of the conditions match
await users.find({
  $nor: [
    { banned: true },
    { deleted: true }
  ]
});

// Combining implicit AND with $or
await users.find({
  status: 'active',  // Implicit AND
  $or: [
    { role: 'admin' },
    { role: 'moderator' }
  ]
});

Special Operators

// $exists - Field exists
await users.find({ email: { $exists: true } });

// $regex - Regular expression
await users.find({ email: { $regex: /@gmail\.com$/ } });

// $mod - Modulo operation (even ages)
await users.find({ age: { $mod: [2, 0] } });

Array Queries

// Match value in array
await users.find({ tags: 'premium' });

// Match any value
await users.find({ tags: { $in: ['vip', 'premium'] } });

Nested Fields (Dot Notation)

await users.find({ 'settings.theme': 'dark' });

await users.find({ 'address.city': 'Paris' });

Pagination

daviDB provides flexible pagination with automatic metadata.

Basic Pagination

const db = new (require('davidb'))({ debug: true });

const productList = [];
for (let i = 1; i <= 50; i++) {
    productList.push({
        name: `Product ${i}`,
        price: Math.floor(Math.random() * 1000) + 10,
        category: ['Electronics', 'Clothing', 'Books', 'Home'][Math.floor(Math.random() * 4)],
        stock: Math.floor(Math.random() * 100)
    });
}

db.collection('products').insertMany(productList)
    .catch(e => { console.error('Error while inserting products: ', e); });

db.collection('products').find({}, { limit: 3, page: 4 })
    .then(({ documents: products, pagination }) => {
        console.log('All products : ', products);
    })
    .catch(e => { console.error('Error while finding products: ', e); });

db.collection('products')
    .count({ category: { $in: ['Electronics', 'Books'] } })
    .then((productsCount) => {
        console.log('Products count in Electronics or Books category: ', productsCount);
    })
    .catch(e => console.error('Error while counting products: ', e));

Using Skip

const db = new (require('davidb'))({ debug: true });

db.collection('products').find({}, { limit: 2, skip: 40 })
    .then(({ documents: products, pagination }) => {
        console.log('All products : ', products);
    })
    .catch(e => { console.error('Error while finding products: ', e); });

Field Projection

Select or exclude specific fields from query results.

Include Fields

const db = new (require('davidb'))({ debug: true });

db.collection('products').find({}, { fields: { id: 1, name: 1, price: 1 } })
    .then(({ documents: products, pagination }) => {
        console.log('All products : ', products);
    })
    .catch(e => { console.error('Error while finding products: ', e); });

Exclude Fields

const db = new (require('davidb'))({ debug: true });

db.collection('products').find({}, { fields: { name: -1, price: -1 } })
    .then(({ documents: products, pagination }) => {
        console.log('All products : ', products);
    })
    .catch(e => { console.error('Error while finding products: ', e); });

Note: Cannot mix inclusion and exclusion in the same projection.

Database Management

Manage collections and database status.

List Collections

const db = new (require('davidb'))({ debug: true });

db.listCollections()
    .then(({ collections }) => { console.log('All collections: ', collections) });

Get Collection Status

const db = new (require('davidb'))({ debug: true });

db.collectionStatus('users')
    .then(console.log);

// or

db.collection('users')
    .status()
    .then(console.log);

Drop Collection

const daviDB = require('davidb');

const db = new daviDB({
    debug: true
});

db.collection('users').drop()
    .then(deleted => {
        console.log(deleted);
    })
    .catch(e => { console.log('Error while dropping collection: ', e.message); });

Close Database

db.close()
    .then(() => { console.log('Database closed'); })
    .catch(e => { console.log('Error while closing database: ', e.message); });

// Flush all pending writes and close connections
// By default, daviDB auto close when script finish.
// Use this method only if you want to close it before the end of the script.

Events

daviDB can emit real-time events whenever documents are inserted, updated, deleted, or a collection is dropped. Subscribe with db.subscribe() to react to changes as they happen.

In Local Mode, events are emitted in-process. In HTTP Mode, events are streamed from the server over Server-Sent Events (SSE) — note that HTTPAdapter is currently unavailable (see Introduction).

By default, events are enabled. Use enableEvents: false option to disable them.

const db = new (require('davidb'))({ debug: true, enableEvents: false });

Subscribe to All Events

const db = new (require('davidb'))({ debug: true });

db.subscribe((event) => {
    console.log('Event received: ', event);
});

Subscribe to Specific Collections

const db = new (require('davidb'))({ debug: true });

db.subscribe('users', (event) => {
    console.log('Users collection event: ', event);
});

// Subscribe to all operations from multiple collections
db.subscribe(['users', 'orders'], (event) => {
    console.log('Event: ', event);
});

Subscribe to Specific Operations

const db = new (require('davidb'))({ debug: true });

db.subscribe('users', 'insert', (event) => {
    console.log('New user inserted: ', event);
});

// Subscribe to multiple operations for a collection
db.subscribe('users', ['insert', 'delete'], (event) => {
    console.log('User inserted or deleted: ', event);
});

// Subscribe to all operations for multiple collections
db.subscribe(['users', 'orders'], '*', (event) => {
    console.log('Event: ', event);
});

Allowed operations: insert, update, delete, drop, * (all).

Unsubscribe

Useless to unsubscribe when closing the nodejs application, that will automatically unsubscribe all the listeners.

const db = new (require('davidb'))({ debug: true });

const unsubscribe = db.subscribe('users', (event) => {
    console.log('Event: ', event);
});

// Later, stop listening
unsubscribe();

// Or, unsubscribe by reference
const listener = (event) => console.log('Event: ', event);
db.subscribe('users', listener);
db.unsubscribe(listener);

Event Payload

{
  collection: 'users',
  operation: 'insert',       // 'insert' | 'update' | 'delete' | 'drop'
  emittedAt: 1719000000000,
  method: 'insertOne',       // originating method, e.g. 'insertOne', 'updateMany', 'deleteById'
  duration: 3,                // operation duration in ms
  count: 1,                   // number of documents affected
  ids: [1],                   // affected document ids
  documents: [ /* ... */ ],   // affected documents (omitted if count exceeds maxEventDocuments)
  truncated: false,           // true if documents were omitted due to maxEventDocuments
  filters: { /* ... */ },     // present for update/delete operations
  update: { /* ... */ }       // present for update operations ($set, $inc, etc.)
}

Use maxEventDocuments (default 100) to control how many documents are included in the documents field before it gets truncated. See Configuration.

Error Handling

All operations throw a standardized response format.

Error Object Structure

{
  status: 400,
  code: 'DB:FILTERS:BAD_REQUEST',
  message: 'Invalid filters query >> $existing <<',
  received: '$existing',
  allowedOps: [
    '$eq',     '$ne',
    '$gt',     '$gte',
    '$lt',     '$lte',
    '$in',     '$nin',
    '$exists', '$regex',
    '$and',    '$or',
    '$nor',    '$not',
    '$mod'
  ]
}

{
  status: 400,
  code: 'DB:OPTIONS:BAD_REQUEST',
  message: 'Invalid query option >> field <<',
  received: 'field',
  allowedOps: [ 'sort', 'skip', 'limit', 'fields', 'page', 'returnDocuments' ]
}

{
  status: 400,
  code: 'DB:UPDATE:BAD_REQUEST',
  message: 'Invalid update operator >> $include <<',
  received: '$include',
  allowedOps: [ '$set', '$unset', '$inc', '$push', '$pull', '$addToSet' ]
}

{
  status: 400,
  code: 'DB:UPDATE_BY_ID:BAD_REQUEST',
  message: 'updateById operation requires a valid document ID.'
}

Acknowledgments

A heartfelt thank you to emi.py for his dedicated testing and debugging efforts. His work was instrumental in identifying and fixing critical issues related to:

  • Parallel operations across multiple collections
  • Cache eviction during active operations
  • Collection name handling in concurrent requests
  • Overall stability and performance improvements

daviDB is more reliable thanks to his contributions.


daviDB — Lightweight. Reliable. Tested with passion. 💙