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 🙏

© 2025 – Pkg Stats / Ryan Hefner

jsonion

v1.0.0

Published

A lightweight JSON file-based database with nested data access and manipulation capabilities.

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 jsonion

Quick 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(); // 10

Deep 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/false

Delete 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); // replace

Best 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.