@mohamed_bakr/jsondb
v1.0.0
Published
A production-ready JSON database with ORM-like API
Maintainers
Readme
JsonDB
A production-ready JSON database with ORM-like API for Node.js. Built with TypeScript, designed for simplicity and performance.
Features
- 🔥 ORM-like API — Intuitive methods:
create,find,update,delete - 📁 JSON File Storage — Human-readable, easy to backup and version control
- 🔍 Powerful Querying — Supports
$eq,$ne,$gt,$gte,$lt,$lte,$in,$nin,$regex,$exists,$type, and logical operators$and,$or,$not - 🗂️ Schema Validation — Define schemas with types, required fields, defaults, and custom validators
- 🪝 Hooks System — Pre/post hooks for
create,update,delete,find - 🔄 Transactions — Atomic operations with automatic rollback
- 💾 Backups — Built-in backup and restore functionality
- 🧹 Soft Deletes — Optional soft deletion with
isDeletedflag - ⚡ In-Memory Cache — Automatic caching for fast reads
- 🔐 File Locking — Prevents corruption from concurrent access
- 📦 Zero Dependencies — Lightweight with minimal external dependencies
Installation
npm install jsondbQuick Start
import { JsonDB } from 'jsondb';
// Initialize database
const db = new JsonDB({
dataDir: './data',
autoSave: true,
});
await db.initialize();
// Create a collection
const users = db.collection('users', {
schema: {
name: { type: 'string', required: true },
email: { type: 'string', required: true, unique: true },
age: { type: 'number', default: 0 },
},
});
// Create documents
const alice = await users.create({
name: 'Alice',
email: '[email protected]',
age: 30,
});
// Find documents
const allUsers = await users.find();
const adults = await users.find({ age: { $gte: 18 } });
const aliceByEmail = await users.findOne({ email: { $eq: '[email protected]' } });
// Update
await users.update(alice.id, { age: 31 });
// Delete
await users.delete(alice.id);
// Close database
await db.close();API Reference
JsonDB
Constructor
const db = new JsonDB(config?: JsonDBConfig);Config Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| dataDir | string | './data' | Directory for JSON files |
| autoSave | boolean | true | Automatically save changes to disk |
| prettyPrint | boolean | true | Pretty print JSON files |
| indent | number | 2 | JSON indentation |
| fileLocking | boolean | true | Enable file locking |
| lockTimeout | number | 5000 | Lock timeout in ms |
| backupDir | string | './backups' | Directory for backups |
| maxBackups | number | 10 | Maximum number of backups |
Methods
| Method | Description |
|--------|-------------|
| initialize() | Initialize database and create data directory |
| collection(name, options?) | Get or create a collection |
| listCollections() | List all collections |
| dropCollection(name) | Delete a collection |
| transaction(callback) | Execute operations in a transaction |
| backup() | Create a backup |
| restore(backupPath) | Restore from backup |
| close() | Close database and flush pending writes |
Collection
Methods
| Method | Description | Example |
|--------|-------------|---------|
| create(data, options?) | Create a document | users.create({ name: 'John' }) |
| find(filter?, options?) | Find documents | users.find({ age: { $gte: 18 } }) |
| findOne(filter, options?) | Find first matching document | users.findOne({ email: { $eq: '[email protected]' } }) |
| findById(id) | Find by ID | users.findById(1) |
| update(id, data, options?) | Update by ID | users.update(1, { name: 'Jane' }) |
| updateMany(filter, data) | Update multiple | users.updateMany({ age: { $lt: 18 } }, { status: 'minor' }) |
| delete(id, options?) | Delete by ID | users.delete(1) |
| deleteMany(filter, options?) | Delete multiple | users.deleteMany({ status: { $eq: 'inactive' } }) |
| count(filter?) | Count documents | users.count({ active: true }) |
| exists(filter) | Check existence | users.exists({ email: { $eq: '[email protected]' } }) |
| query() | Start query builder | users.query().where('age').gte(18).find() |
Find Options
interface FindOptions {
sort?: SortOptions; // { age: 'asc' } or { age: 'desc' }
skip?: number; // Pagination offset
limit?: number; // Max results
projection?: Projection; // { name: 1, email: 1 } or { password: 0 }
includeDeleted?: boolean; // Include soft-deleted docs
}Query Operators
Comparison
| Operator | Description | Example |
|----------|-------------|---------|
| $eq | Equal | { age: { $eq: 25 } } |
| $ne | Not equal | { status: { $ne: 'deleted' } } |
| $gt | Greater than | { age: { $gt: 18 } } |
| $gte | Greater than or equal | { age: { $gte: 18 } } |
| $lt | Less than | { age: { $lt: 65 } } |
| $lte | Less than or equal | { age: { $lte: 65 } } |
| $in | In array | { status: { $in: ['active', 'pending'] } } |
| $nin | Not in array | { status: { $nin: ['banned', 'deleted'] } } |
String
| Operator | Description | Example |
|----------|-------------|---------|
| $regex | Regular expression | { name: { $regex: '^John' } } |
| $contains | Contains substring | { name: { $contains: 'John' } } |
| $startsWith | Starts with | { name: { $startsWith: 'John' } } |
| $endsWith | Ends with | { name: { $endsWith: 'Doe' } } |
Logical
| Operator | Description | Example |
|----------|-------------|---------|
| $and | All conditions | { $and: [{ age: { $gte: 18 } }, { status: 'active' }] } |
| $or | Any condition | { $or: [{ role: 'admin' }, { role: 'moderator' }] } |
| $not | Negation | { $not: { status: 'deleted' } } |
Array
| Operator | Description | Example |
|----------|-------------|---------|
| $size | Array length | { tags: { $size: 3 } } |
| $all | Contains all | { tags: { $all: ['typescript', 'nodejs'] } } |
Schema Definition
const users = db.collection('users', {
schema: {
name: { type: 'string', required: true },
email: { type: 'string', required: true, unique: true },
age: { type: 'number', default: 0, min: 0, max: 150 },
role: { type: 'string', enum: ['user', 'admin', 'moderator'] },
tags: { type: 'array', default: [] },
metadata: { type: 'object' },
isActive: { type: 'boolean', default: true },
createdAt: { type: 'date' },
},
options: {
timestamps: true, // Auto add createdAt/updatedAt
softDelete: false, // Enable soft deletes
autoIncrement: true, // Auto-increment IDs
useUUID: false, // Use UUIDs instead of numbers
},
});Hooks
// Pre-create hook
users.before('create', async (data) => {
data.slug = data.name.toLowerCase().replace(/\s+/g, '-');
return data;
});
// Post-create hook
users.after('create', async (doc) => {
console.log(`Created user: ${doc.name}`);
});
// Pre-update hook
users.before('update', async (data) => {
data.updatedAt = new Date().toISOString();
return data;
});Transactions
await db.transaction(async () => {
const alice = await users.findById(1);
const bob = await users.findById(2);
await users.update(alice.id, { balance: alice.balance - 100 });
await users.update(bob.id, { balance: bob.balance + 100 });
// If any operation fails, all changes are rolled back
});Backups
// Create backup
const backupPath = await db.backup();
console.log(`Backup created: ${backupPath}`);
// Restore from backup
await db.restore(backupPath);Examples
See the examples directory for more usage examples.
Testing
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # With coverage reportPerformance
npm run test:performance # Run benchmarksContributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
