A lightweight, type-safe JSON database for Node.js with a fluent query interface and ACID-compliant transactions.
Type-Safe: Full TypeScript support with generics
Fluent Queries: Intuitive query builder with multiple syntax styles
ACID Transactions: Atomic operations with automatic rollback
Concurrency Control: Built-in locking to prevent race conditions
Zero Dependencies: No external libraries (except Node.js built-ins)
Easy to Use: Simple API that's easy to learn
npm install luxdbimport { LuxDB } from 'luxdb';
// Define your data structure
interface User {
id: string;
name: string;
age: number;
status: 'active' | 'inactive';
}
const db = await LuxDB.create<User>('users');
await db.insert({ id: '1', name: 'Alice', age: 25, status: 'active' });
const user = await db.getOne().where({ name: 'Alice' });
const adults = await db.getAll().where('age', '>=', 18);
await db.updateOne({ status: 'inactive' }).where('id', '=', '1');
await db.deleteOne().where('id', '=', '1');LuxDB supports multiple query syntaxes for flexibility:
// Single condition
await db.getOne().where({ name: 'Alice' });
// Multiple conditions
await db.getAll().where({ status: 'active', age: 25 });// Comparison operators
await db.getAll().where('age', '>', 18);
await db.getAll().where('age', '<=', 65);
await db.getAll().where('name', '!=', 'Alice');
// IN operator
await db.getAll().where('status', 'in', ['active', 'pending']);
// BETWEEN operator
await db.getAll().where('age', 'between', [18, 65]);
// MATCHES operator (regex)
await db.getAll().where('email', 'matches', /.*@example\.com$/);await db.getAll()
.where({ status: 'active' })
.where('age', '>', 25);| Operator | Symbol | Example |
|---|---|---|
| Equals | = |
.where('age', '=', 25) |
| Not Equal | != |
.where('status', '!=', 'inactive') |
| Greater Than | > |
.where('age', '>', 18) |
| Less Than | < |
.where('age', '<', 65) |
| Greater or Equal | >= |
.where('age', '>=', 18) |
| Less or Equal | <= |
.where('age', '<=', 65) |
| In | in |
.where('status', 'in', ['active', 'pending']) |
| Between | between |
.where('age', 'between', [18, 65]) |
| Matches | matches |
.where('email', 'matches', /pattern/) |
Select specific fields instead of returning entire objects:
// Get only name and email
const users = await db.getAll('name', 'email').where({ status: 'active' });
// Returns: [{ name: 'Alice', email: 'alice@example.com' }, ...]
// Single field
const names = await db.getAll('name');
// Returns: [{ name: 'Alice' }, { name: 'Bob' }, ...]Perform multiple operations atomically with automatic rollback on failure:
const tx = await db.beginTransaction();
try {
// Add operations
tx.insert({ id: '1', name: 'Alice', age: 25, status: 'active' });
tx.update(user => user.id === '2', { status: 'active' });
tx.delete(user => user.age < 18);
// Commit all changes atomically
await tx.commit();
} catch (error) {
// Automatic rollback on error
await tx.rollback();
}Create and initialize a new database instance.
const db = await LuxDB.create<User>('users', './data');Insert one or more items.
await db.insert({ id: '1', name: 'Alice' });
await db.insert([{ id: '2', name: 'Bob' }, { id: '3', name: 'Charlie' }]);Find a single item. Returns null if not found.
const user = await db.getOne().where({ id: '1' });
const userInfo = await db.getOne('name', 'email').where({ id: '1' });Find all matching items. Returns empty array if none found.
const users = await db.getAll().where({ status: 'active' });
const names = await db.getAll('name').where({ status: 'active' });Update a single matching item. Returns the updated item or null.
const updated = await db.updateOne({ status: 'inactive' })
.where('id', '=', '1');Update all matching items. Returns array of updated items.
const updated = await db.updateAll({ status: 'active' })
.where('age', '>=', 18);Delete a single matching item. Returns the deleted item or null.
const deleted = await db.deleteOne().where('id', '=', '1');Delete all matching items. Returns array of deleted items.
const deleted = await db.deleteAll().where({ status: 'inactive' });Start a new transaction.
const tx = await db.beginTransaction();Delete all data from the database. Use with caution!
await db.clear();Get the number of items in the database.
console.log(`Database has ${db.size} items`);Add insert operation to transaction.
Add update operation to transaction.
Add delete operation to transaction.
Commit all operations atomically.
Rollback all operations and restore original state.
LuxDB provides specific error types for different scenarios:
import {
DatabaseError,
FileNotFoundError,
TransactionError,
LockError
} from 'luxdb';
try {
await db.insert({ id: '1', name: 'Alice' });
} catch (error) {
if (error instanceof LockError) {
console.error('Failed to acquire lock');
} else if (error instanceof DatabaseError) {
console.error('Database operation failed:', error.message);
}
}LuxDB follows clean architecture principles:
- Separation of Concerns: Database logic, storage, and queries are separated
- SOLID Principles: Each class has a single responsibility
- Concurrency Control: Mutex locks prevent race conditions
- ACID Compliance: Transactions ensure data integrity
- In-Memory: All data is kept in memory for fast access
- Lazy Write: Data is written to disk only when modified
- Locking: Ensures data consistency but may impact concurrent writes
- JSON Serialization: Uses native JSON.stringify/parse for efficiency
- Not for Large Datasets: Best for small to medium datasets (< 100k items)
- No Indexing Yet: Sequential search for queries (O(n) complexity)
- Single File: All data in one JSON file
- No Sharding: Not designed for distributed systems
- Node.js Only: Requires Node.js filesystem APIs
- Indexing for faster queries
- Query optimization
- Compression support
- Schema validation
- Migrations
- Backup/restore utilities
- Watch mode for real-time updates
Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - see LICENSE file for details