jsonion
v1.0.0
Published
A lightweight JSON file-based database with nested data access and manipulation capabilities.
Maintainers
Readme
JSONion 🚀
A powerful and intuitive JSON-based database for Node.js with deep nested data access capabilities.
Features ✨
- 📁 File-based JSON storage - Simple and portable
- 🎯 Deep path access - Easy navigation through nested objects
- 🔍 Advanced search - Find data at any depth
- 📦 Array operations - Manipulate nested arrays effortlessly
- 🔄 Auto-save - Automatic data persistence
- 🛡️ Type-safe - Full TypeScript support
- 🚀 Zero dependencies - Lightweight and fast
Installation
npm install jsonionQuick Start
import { JSONion } from 'jsonion';
// Create or open a database
const db = new JSONion('mydata.json');
// Simple operations
db.set('user', { name: 'Ahmed', age: 25 });
const user = db.get('user');
// Deep path access
db.setPath('user.profile.address.city', 'Casablanca');
const city = db.getPath('user.profile.address.city');
// Returns: 'Casablanca'Core API
Basic Operations
// Set a value
db.set('key', value);
// Get a value
const value = db.get('key');
// Check if key exists
db.has('key'); // true/false
// Delete a key
db.del('key');
// Get all keys
db.keys(); // ['key1', 'key2', ...]
// Get all data
db.all(); // { key1: value1, key2: value2, ... }
// Clear all data
db.clear();
// Count entries
db.count(); // 10Deep Path Access 🎯
The most powerful feature of JSONion - access deeply nested data with ease!
Get Path
Access nested data using dot notation and array indices:
// Simple nested object
db.setPath('user.name', 'Ahmed');
db.getPath('user.name'); // 'Ahmed'
// Deep nesting
db.setPath('config.database.connections[0].host', 'localhost');
db.getPath('config.database.connections[0].host'); // 'localhost'
// Complex paths
db.getPath('users[0].posts[2].comments[5].text');Set Path
Create or update values at any depth (automatically creates missing paths):
// Creates the entire structure if it doesn't exist
db.setPath('user.profile.settings.theme', 'dark');
// Works with arrays
db.setPath('products[0].price', 99.99);
db.setPath('users[0].roles[1]', 'admin');Has Path
Check if a path exists:
db.hasPath('user.profile.verified'); // true/false
db.hasPath('config.database.host'); // true/falseDelete Path
Remove data at a specific path:
db.deletePath('user.oldField');
db.deletePath('settings.deprecated.feature');Update Path
Update existing values (only if path exists):
// Simple update
db.updatePath('user.age', 26);
// Update with function
db.updatePath('product.price', (oldPrice) => oldPrice * 1.1);Advanced Search 🔍
Find Deep
Search for a key-value pair at any level:
// Find all occurrences of email
db.findDeep('email', '[email protected]');
// Returns: [
// { path: 'users[0].email', value: '[email protected]' },
// { path: 'contacts[3].email', value: '[email protected]' }
// ]
// Find all active statuses
db.findDeep('status', 'active');Pluck Path
Extract values using wildcards:
// Get all user names
db.pluckPath('users[*].name');
// Returns: ['Ahmed', 'Sara', 'Omar']
// Get all prices
db.pluckPath('products[*].price');
// Returns: [29.99, 49.99, 19.99]
// Get nested values
db.pluckPath('orders[*].user.country');
// Returns: ['MA', 'EG', 'MA']Find In Path
Search within arrays with conditions:
// Find with function
db.findInPath('users', (user) => user.age > 18);
// Find with object matching
db.findInPath('products', { available: true, category: 'electronics' });
// Find with operators
db.findInPath('products', {
price: { $lt: 100, $gt: 50 },
stock: { $gte: 10 }
});Supported operators:
$lt- Less than$lte- Less than or equal$gt- Greater than$gte- Greater than or equal$ne- Not equal$in- Value in array$nin- Value not in array
Array Operations 📦
Push To Path
Add items to nested arrays:
// Add a post to user's posts
db.pushToPath('user.posts', {
title: 'New Post',
content: 'Content here',
date: new Date()
});
// Add a tag
db.pushToPath('article.tags', 'javascript');Pull From Path
Remove items from arrays:
// Remove posts with specific ID
const removed = db.pullFromPath('user.posts', (post) => post.id === 5);
console.log(`Removed ${removed} items`);
// Remove old notifications
db.pullFromPath('notifications', (n) => n.read === true);Update In Path
Update specific items in arrays:
// Update with object condition
db.updateInPath('users',
{ id: 1 },
{ status: 'active', lastLogin: new Date() }
);
// Update with function condition
db.updateInPath('products',
(product) => product.stock < 10,
{ lowStock: true }
);Data Manipulation 🔄
Merge Path
Merge objects without losing existing data:
// Merge into existing object
db.mergePath('user.profile', {
bio: 'New bio',
avatar: 'avatar.jpg'
});
// Keeps existing fields like 'name', 'email', etc.Copy Path
Copy data from one path to another:
// Create a copy
db.copyPath('template.config', 'user.config');
// Duplicate an item
db.copyPath('products[0]', 'products[10]');Move Path
Move data (copy + delete original):
// Rename a path
db.movePath('user.oldSettings', 'user.settings');
// Reorganize data
db.movePath('temp.data', 'permanent.data');Complete Example
import { JSONion } from 'jsonion';
const db = new JSONion('blog.json');
// Create a blog post structure
db.setPath('posts[0]', {
id: 1,
title: 'Getting Started with JSONion',
author: {
name: 'Ahmed',
email: '[email protected]'
},
tags: ['javascript', 'database'],
comments: []
});
// Add a comment
db.pushToPath('posts[0].comments', {
user: 'Sara',
text: 'Great article!',
date: new Date()
});
// Find all posts by Ahmed
const ahmedPosts = db.findInPath('posts',
(post) => post.author.name === 'Ahmed'
);
// Get all post titles
const titles = db.pluckPath('posts[*].title');
// Update post
db.updatePath('posts[0].title', 'JSONion: A Complete Guide');
// Search for email anywhere
const emailLocations = db.findDeep('email', '[email protected]');
// Merge author data
db.mergePath('posts[0].author', {
bio: 'Software Developer',
website: 'ahmed.dev'
});
// Always flush before exit
db.flush();Database Management
Manager API
import { Manager } from 'jsonion';
const manager = new Manager('./databases');
// Create a new database
const db = manager.create('users.json', { initialized: true });
// Open existing database
const db2 = manager.open('products.json');
// Check if database exists
if (manager.exists('config.json')) {
// ...
}
// List all databases
const files = manager.list(); // ['users.json', 'products.json']
// Delete a database
manager.delete('old.json');
// Rename a database
manager.rename('old.json', 'new.json');
// Copy a database
manager.copy('template.json', 'new-project.json');
// Get database info
const info = manager.info('users.json');
// { path: '...', size: 1024, entries: 50, modified: Date }
// Merge multiple databases
manager.merge(
['db1.json', 'db2.json', 'db3.json'],
'merged.json'
);Advanced Features
Find with Predicate
// Find entries matching condition
const adults = db.find((value, key) => {
return value.age && value.age >= 18;
});
// Find entries (returns [key, value] pairs)
const entries = db.findEntries((value, key) => value.verified === true);Batch Operations
// Get multiple values
const [name, email, age] = db.getMany('name', 'email', 'age');
// Check multiple keys
if (db.hasAll('username', 'email', 'password')) {
// All keys exist
}
// Delete multiple keys
const deleted = db.deleteMany('temp1', 'temp2', 'cache');
console.log(`Deleted ${deleted} keys`);Import/Export
// Export to JSON string
const jsonString = db.export();
// Import from object
db.import({ key1: 'value1', key2: 'value2' }, true); // merge
// Import from JSON string
db.import('{"key": "value"}', false); // replaceBest Practices 💡
1. Always flush before exiting
process.on('SIGINT', () => {
db.flush();
process.exit(0);
});2. Use path notation for nested data
// ❌ Bad
const user = db.get('user');
user.profile.settings.theme = 'dark';
db.set('user', user);
// ✅ Good
db.setPath('user.profile.settings.theme', 'dark');3. Use wildcards for bulk operations
// Get all values at once
const allEmails = db.pluckPath('users[*].email');
// Instead of looping
const users = db.get('users');
const emails = users.map(u => u.email);4. Use operators for complex queries
// Clean and readable
db.findInPath('products', {
price: { $gte: 10, $lte: 100 },
category: { $in: ['electronics', 'gadgets'] }
});API Reference
Database Class
| Method | Description |
|--------|-------------|
| set(key, value) | Set a key-value pair |
| get(key) | Get a value by key |
| del(key) | Delete a key |
| has(key) | Check if key exists |
| keys() | Get all keys |
| all() | Get all data |
| clear() | Clear all data |
| count() | Count entries |
| update(key, value) | Update existing key |
| find(predicate) | Find values matching predicate |
| flush() | Force save to disk |
| reload() | Reload from disk |
Deep Access Methods
| Method | Description |
|--------|-------------|
| getPath(path) | Get value at path |
| setPath(path, value) | Set value at path |
| hasPath(path) | Check if path exists |
| deletePath(path) | Delete path |
| updatePath(path, value) | Update path |
| findDeep(key, value) | Find key-value at any depth |
| pluckPath(pattern) | Extract values with wildcards |
| findInPath(path, predicate) | Find in array at path |
| pushToPath(path, value) | Add to array at path |
| pullFromPath(path, predicate) | Remove from array |
| updateInPath(path, find, update) | Update array item |
| mergePath(path, data) | Merge object at path |
| copyPath(from, to) | Copy path |
| movePath(from, to) | Move path |
TypeScript Support
JSONion is written in TypeScript and includes full type definitions:
interface User {
name: string;
email: string;
age: number;
}
// Type-safe operations
db.set<User>('user', { name: 'Ahmed', email: '[email protected]', age: 25 });
const user = db.get<User>('user');
// Type-safe path access
const name = db.getPath<string>('user.name');
const age = db.getPath<number>('user.age');Error Handling
import { JSONionError } from 'jsonion';
try {
db.setPath('user.name', 'Ahmed');
} catch (error) {
if (error instanceof JSONionError) {
console.error(`Operation: ${error.operation}`);
console.error(`Message: ${error.message}`);
}
}Performance Tips
- Use
flush()manually for critical operations - Batch related operations together
- Use path methods instead of get-modify-set cycles
- Use
reload()if external changes are made
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Support
For issues and questions, please open an issue on GitHub.
Made with ❤️ for developers who love simple, powerful tools.
