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

dapbase

v3.0.3

Published

A dead-simple, file-based, folder-visible database for Node.js — now with relationships, schema validation, and encryption

Readme

Dapbase v3.0

A dead-simple, file-based, folder-visible database for Node.js — now with relationships, schema validation, and encryption

No server. No binaries. No black boxes.
Your data lives in plain files inside a Dapbase/ folder you can open, read, backup, or edit by hand if you ever want to.

Perfect for small apps, prototypes, CLI tools, learning, or when you just want full control.


🚀 What's New in v3.0

  • Auto-Generated Timestamps - createdAt and updatedAt automatically managed
  • Schema Validation - Type checking, constraints, and defaults
  • Relationships - Foreign keys with cascade delete
  • Advanced Query Operators - $gt, $lt, $gte, $lte, $in, $like, $regex
  • Encryption - Field-level AES-256 encryption
  • Backup System - One-command backups and restores
  • Indexes - Performance optimization
  • Migrations - Safe data transformations
  • 100% Test Coverage - Battle-tested with 40+ comprehensive tests

✨ Features

  • Zero dependencies (core) - Just Node.js, nothing else
  • 100% transparent - See every table as readable .table files
  • Multiple databases - Just create folders
  • Schema Validation - Enforce data integrity with constraints
  • Auto-Timestamps - createdAt and updatedAt handled automatically
  • Relationships - Model complex data with foreign keys
  • Encryption - Optional field-level security
  • Query Power - Advanced filtering with operators, joins, and pagination
  • Atomic Writes - Data integrity with write-file-atomic
  • Type Safety - Runtime type checking with custom validators
  • Migration Support - Safe data transformations

📦 Installation

npm install dapbase

Or try it immediately:

npx dapbase@latest init

The installer automatically creates a Dapbase/ folder in your project with everything you need.


📁 Folder Structure After Install

Dapbase/
├── dapbase.config.json          # Global configuration
├── dapbase.connection.js        # Import this in your app
├── .encryption.key              # Auto-generated encryption key (if enabled)
└── main/                        # Your first database (auto-created)
    ├── users.table
    ├── posts.table
    └── ...                      # Your tables appear here as readable files

🎮 Quick Start

1. Initialize Your Project

npx dapbase init

Follow the interactive prompts to set up your database.

2. Use in Your Code

// app.js
const db = require('./Dapbase/dapbase.connection.js');

(async () => {
  // Switch to a database (creates folder if needed)
  await db.use('blog_db');

  // Create a table with schema validation
  await db.createTable('users', {
    name: { type: 'string', required: true },
    email: { type: 'string', unique: true, pattern: '^[^@]+@[^@]+\\.[^@]+$' },
    age: { type: 'integer', min: 0, max: 150 },
    status: { type: 'string', default: 'active' }
  });

  // Insert data - timestamps are auto-generated!
  const user = await db.insert('users', {
    name: 'John Doe',
    email: '[email protected]',
    age: 30
  });
  
  console.log(user);
  // {
  //   id: 'uuid-generated',
  //   name: 'John Doe',
  //   email: '[email protected]',
  //   age: 30,
  //   createdAt: '2026-01-16T12:00:00.000Z',
  //   updatedAt: '2026-01-16T12:00:00.000Z'
  // }

  // Query with advanced filters
  const activeUsers = await db.select('users', {
    where: { 
      status: 'active',
      age: { $gte: 18, $lte: 65 }
    },
    orderBy: ['name', 'asc'],
    limit: 10,
    offset: 0
  });

  console.log(activeUsers);
})();

📖 Core API

Database Operations

// Switch to/create database
await db.use('database_name');

Table Management

// Create table with schema
await db.createTable('users', {
  username: { type: 'string', required: true, unique: true },
  email: { type: 'string', required: true },
  age: { type: 'integer', min: 0, max: 150 }
});

// Create table without auto-timestamps
await db.createTable('logs', {
  message: { type: 'string' }
}, {}, { timestamps: false });

// Create table with relationships
await db.createTable('posts', {
  title: { type: 'string', required: true },
  content: { type: 'text' },
  userId: { type: 'uuid' }
}, {
  userId: { foreignTable: 'users', foreignKey: 'id' }
});

// Add column to existing table
await db.addColumn('users', 'bio', { type: 'text', default: '' });

// Remove column
await db.removeColumn('users', 'old_field');

Data Operations

// Insert single row (auto-generates id, createdAt, updatedAt)
const user = await db.insert('users', { 
  username: 'jane_doe',
  email: '[email protected]',
  age: 28
});

// Insert multiple rows
const users = await db.insertMany('users', [
  { username: 'alice', email: '[email protected]', age: 25 },
  { username: 'bob', email: '[email protected]', age: 30 }
]);

// Query with operators
const users = await db.select('users', {
  where: {
    age: { $gte: 18, $lt: 65 },
    email: { $like: '@example.com' },
    status: { $in: ['active', 'pending'] }
  },
  orderBy: ['createdAt', 'desc'],
  limit: 20,
  offset: 0,
  fields: ['username', 'email'] // Select specific fields
});

// Update (automatically updates updatedAt timestamp)
await db.update('users', 
  { status: 'inactive' }, 
  { age: { $lt: 18 } }
);

// Delete with cascade option
await db.delete('users', { status: 'banned' }, { cascade: true });

Query Operators

// Comparison operators
where: {
  age: { $eq: 25 },        // Equal to
  age: { $ne: 25 },        // Not equal to
  age: { $gt: 18 },        // Greater than
  age: { $gte: 18 },       // Greater than or equal
  age: { $lt: 65 },        // Less than
  age: { $lte: 65 },       // Less than or equal
  status: { $in: ['active', 'pending'] },     // In array
  status: { $nin: ['banned', 'deleted'] },    // Not in array
  email: { $like: '@example.com' },           // Contains string
  username: { $regex: '^admin' }              // Regex pattern
}

// Combine multiple operators
where: {
  age: { $gte: 18, $lte: 65 },
  email: { $like: '@company.com' }
}

Relationships & Joins

// Query with join
const postsWithAuthors = await db.select('posts', {
  join: [{
    table: 'users',
    on: { local: 'userId', foreign: 'id' },
    as: 'author',
    type: 'left' // or 'inner'
  }],
  where: { status: 'published' }
});

// Result structure:
// [
//   {
//     id: 'post-uuid',
//     title: 'My Post',
//     userId: 'user-uuid',
//     author: {
//       id: 'user-uuid',
//       username: 'john_doe',
//       email: '[email protected]'
//     }
//   }
// ]

🔐 Schema Validation

Available Types

{
  // String types
  name: { type: 'string' },     // Short text
  content: { type: 'text' },    // Long text
  
  // Numeric types
  age: { type: 'integer' },     // Whole numbers
  age: { type: 'int' },         // Alias for integer
  price: { type: 'float' },     // Decimal numbers
  score: { type: 'number' },    // Any number
  
  // Other types
  active: { type: 'boolean' },
  id: { type: 'uuid' },
  createdAt: { type: 'timestamp' },
  birthday: { type: 'date' },
  metadata: { type: 'json' }
}

Constraints

{
  // Required fields
  email: { type: 'string', required: true },
  
  // Unique values
  username: { type: 'string', unique: true },
  
  // Numeric constraints
  age: { type: 'integer', min: 0, max: 150 },
  price: { type: 'float', min: 0 },
  
  // String length
  password: { type: 'string', minLength: 8, maxLength: 100 },
  
  // Pattern matching (regex)
  email: { 
    type: 'string', 
    pattern: '^[^@]+@[^@]+\\.[^@]+$' 
  },
  
  // Default values
  status: { type: 'string', default: 'active' },
  score: { type: 'integer', default: 0 },
  tags: { type: 'json', default: [] }
}

Auto-Generated Fields

// These are automatically added unless timestamps: false
{
  id: { type: 'uuid', required: true },           // Auto-generated UUID
  createdAt: { type: 'string', default: 'NOW' },  // Auto-set on insert
  updatedAt: { type: 'string', default: 'NOW' }   // Auto-updated on update
}

// Disable timestamps for a table
await db.createTable('logs', {
  message: { type: 'string' }
}, {}, { timestamps: false });

🔐 Encryption

// Configure encryption in dapbase.config.json or programmatically
await db.createTable('secrets', {
  publicData: { type: 'string' },
  privateData: { type: 'string' }
}, {}, {
  encryption: {
    fields: ['privateData'] // Only encrypt sensitive fields
  }
});

// Insert data (auto-encrypts privateData)
await db.insert('secrets', {
  publicData: 'Everyone can see this',
  privateData: 'This is encrypted in the file'
});

// Query (auto-decrypts on read)
const secrets = await db.select('secrets');
console.log(secrets[0].privateData); // Decrypted automatically

📊 Real-World Examples

Example 1: Blog Application

const db = require('./Dapbase/dapbase.connection.js');

async function setupBlog() {
  await db.use('blog');
  
  // Users table
  await db.createTable('users', {
    username: { type: 'string', unique: true, required: true },
    email: { type: 'string', unique: true, required: true },
    passwordHash: { type: 'string', required: true },
    role: { type: 'string', default: 'user' }
  });
  
  // Posts table with foreign key
  await db.createTable('posts', {
    title: { type: 'string', required: true, maxLength: 200 },
    slug: { type: 'string', unique: true },
    content: { type: 'text', required: true },
    status: { type: 'string', default: 'draft' },
    userId: { type: 'uuid', required: true }
  }, {
    userId: { foreignTable: 'users', foreignKey: 'id' }
  });
  
  // Comments table
  await db.createTable('comments', {
    content: { type: 'text', required: true },
    postId: { type: 'uuid', required: true },
    userId: { type: 'uuid', required: true }
  }, {
    postId: { foreignTable: 'posts', foreignKey: 'id' },
    userId: { foreignTable: 'users', foreignKey: 'id' }
  });
}

// Get published posts with authors
async function getPublishedPosts(page = 1, limit = 10) {
  const offset = (page - 1) * limit;
  
  return await db.select('posts', {
    where: { status: 'published' },
    join: [{
      table: 'users',
      on: { local: 'userId', foreign: 'id' },
      as: 'author',
      type: 'left'
    }],
    orderBy: ['createdAt', 'desc'],
    limit,
    offset
  });
}

// Create a post with validation
async function createPost(userId, postData) {
  return await db.insert('posts', {
    ...postData,
    userId,
    slug: postData.title.toLowerCase().replace(/\s+/g, '-')
  });
}

Example 2: E-commerce Store

async function setupStore() {
  await db.use('store');
  
  // Products
  await db.createTable('products', {
    name: { type: 'string', required: true },
    sku: { type: 'string', unique: true, required: true },
    price: { type: 'float', min: 0, required: true },
    stock: { type: 'integer', min: 0, default: 0 },
    category: { type: 'string' }
  });
  
  // Orders
  await db.createTable('orders', {
    userId: { type: 'uuid', required: true },
    status: { type: 'string', default: 'pending' },
    total: { type: 'float', min: 0 }
  }, {
    userId: { foreignTable: 'users', foreignKey: 'id' }
  });
  
  // Order items
  await db.createTable('orderItems', {
    orderId: { type: 'uuid', required: true },
    productId: { type: 'uuid', required: true },
    quantity: { type: 'integer', min: 1, required: true },
    price: { type: 'float', min: 0, required: true }
  }, {
    orderId: { foreignTable: 'orders', foreignKey: 'id' },
    productId: { foreignTable: 'products', foreignKey: 'id' }
  });
}

// Get low stock products
async function getLowStockProducts(threshold = 10) {
  return await db.select('products', {
    where: { stock: { $lte: threshold } },
    orderBy: ['stock', 'asc']
  });
}

⚡ Performance Optimization

Indexes

// Add index for faster queries
await db.addIndex('users', 'email', { type: 'hash', unique: true });
await db.addIndex('posts', 'createdAt', { type: 'value' });

// Indexes are automatically used in queries
const user = await db.select('users', {
  where: { email: '[email protected]' }
}); // Fast lookup via index

Field Selection

// Only fetch needed fields
const users = await db.select('users', {
  fields: ['id', 'username', 'email'], // Don't load all fields
  limit: 100
});

Pagination

// Efficient pagination
function getPage(page = 1, pageSize = 20) {
  return db.select('posts', {
    limit: pageSize,
    offset: (page - 1) * pageSize,
    orderBy: ['createdAt', 'desc']
  });
}

🔄 Data Migrations

// Add new field to existing rows
await db.addColumn('users', 'fullName', { type: 'string', default: '' });

// Populate new field from existing data
await db.migrate('users', (row) => ({
  ...row,
  fullName: `${row.firstName || ''} ${row.lastName || ''}`.trim()
}));

// Remove old fields
await db.removeColumn('users', 'firstName');
await db.removeColumn('users', 'lastName');

🧪 Testing

Dapbase includes a comprehensive test suite with 40+ tests covering all features:

# Run the test suite
node comprehensive-test.js

Test Coverage:

  • ✅ Database operations
  • ✅ Table creation with schemas
  • ✅ Insert with validation
  • ✅ Query operators ($gt, $lt, $in, $like, etc.)
  • ✅ Updates with validation
  • ✅ Delete operations
  • ✅ Foreign key constraints
  • ✅ Joins and relationships
  • ✅ Cascade deletes
  • ✅ Schema operations
  • ✅ Migrations
  • ✅ Edge cases

📈 Performance Tips

  1. Use indexes on frequently queried fields (email, username, etc.)
  2. Select specific fields instead of fetching entire rows
  3. Use pagination for large result sets
  4. Batch operations with insertMany for bulk data
  5. Enable encryption only for sensitive fields
  6. Split large tables by date ranges or categories
  7. Regular backups keep your database healthy

🐛 Debugging

# Inspect table files directly
cat Dapbase/blog/users.table | jq '.'

# Check table structure
cat Dapbase/blog/users.table | jq '.columns'

# View all rows
cat Dapbase/blog/users.table | jq '.rows'

🔧 Configuration

Edit Dapbase/dapbase.config.json:

{
  "project": "My Awesome App",
  "environment": "development",
  "defaultDatabase": "main",
  "encryptionEnabled": false,
  "logLevel": "info",
  "maxFileSizeMB": 10
}

🚨 Migration from v2

All v2 APIs are backward compatible:

// Old v2 code (still works)
await db.createTable('users', { name: 'text' });

// New v3 code (recommended - enables auto-timestamps)
await db.createTable('users', { 
  name: { type: 'string', required: true } 
});

New in v3:

  • Auto-generated id, createdAt, updatedAt fields
  • Query operators ($gt, $lt, $in, etc.)
  • Foreign key validation
  • Enhanced schema validation

🤝 Contributing

Dapbase is built slowly, intentionally, and in public.

Ways to contribute:

  • Report bugs or suggest features
  • Improve documentation
  • Share your use cases
  • Submit pull requests
  • Create examples or tutorials

📄 License

MIT © 2025 Duby


🙏 Acknowledgments

Dapbase stands on the shoulders of giants:

  • SQLite for inspiration in simplicity
  • Lowdb for the file-based approach
  • Prisma for schema validation ideas
  • write-file-atomic for atomic writes
  • Every developer who believes data should be visible

🆘 Getting Help


Dapbase — Because your data should be yours.
Simple. Visible. Powerful. Tested.

"If you can't ls your database and understand it, it's not simple enough."