Database using JSON file as storage for Node.JS
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Advanced functionality including array operations, search capabilities, database management, locking, and path utilities for complex data scenarios.
Returns the number of elements in an array at the specified DataPath.
/**
* Returns the number of element which constitutes the array
* @param dataPath - Path to the array
* @returns Promise resolving to array length
* @throws DataError if the path doesn't point to an array
*/
count(dataPath: string): Promise<number>;Usage Examples:
// Count users
const userCount = await db.count("/users");
// Count nested array
const tagCount = await db.count("/posts/1/tags");
// Works with multi-dimensional arrays
const matrixRows = await db.count("/matrix");
const matrixCols = await db.count("/matrix/0");Finds the index of an object in an array based on a property value.
/**
* Returns the index of the object that meets the criteria submitted. Returns -1, if no match is found.
* @param dataPath - Base dataPath from where to start searching
* @param searchValue - Value to look for in the specified property
* @param propertyName - Name of the property to look for searchValue (default: "id")
* @returns Promise resolving to index or -1 if not found
*/
getIndex(
dataPath: string,
searchValue: string | number,
propertyName?: string
): Promise<number>;Usage Examples:
// Find user by ID (default property name)
const userIndex = await db.getIndex("/users", 123);
// Find user by email
const emailIndex = await db.getIndex("/users", "alice@example.com", "email");
// Find post by title
const postIndex = await db.getIndex("/posts", "My First Post", "title");
// Use result for further operations
if (userIndex !== -1) {
const user = await db.getData(`/users/${userIndex}`);
}Finds the index of a primitive value in an array.
/**
* Return the index of the value inside the array. Returns -1, if no match is found.
* @param dataPath - Base dataPath from where to start searching
* @param searchValue - Value to look for in the array
* @returns Promise resolving to index or -1 if not found
*/
getIndexValue(dataPath: string, searchValue: string | number): Promise<number>;Usage Examples:
// Find value in simple array
const tagIndex = await db.getIndexValue("/posts/1/tags", "javascript");
// Find number in array
const scoreIndex = await db.getIndexValue("/game/scores", 1500);
// Use with array operations
if (tagIndex === -1) {
await db.push("/posts/1/tags", ["javascript"], false); // Add if not exists
}Searches for all entries matching a callback predicate function in arrays or objects.
/**
* Find all specific entry in an array/object
* @param rootPath - Base dataPath from where to start searching
* @param callback - Method to filter the result and find the wanted entry. Receive the entry and its index.
* @returns Promise resolving to array of matching entries or undefined if none found
* @throws DataError if the path doesn't point to an array or object
*/
filter<T>(rootPath: string, callback: FindCallback): Promise<T[] | undefined>;Usage Examples:
import { FindCallback } from "node-json-db";
// Filter active users
const activeUsers = await db.filter<User>("/users", (user, index) => {
return user.active === true;
});
// Filter posts by author
const alicePosts = await db.filter<Post>("/posts", (post, index) => {
return post.author === "Alice";
});
// Filter with complex conditions
const recentPosts = await db.filter<Post>("/posts", (post, index) => {
const oneWeekAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
return new Date(post.createdAt).getTime() > oneWeekAgo;
});
// Filter object properties (index will be property name)
const configs = await db.filter<any>("/settings", (value, key) => {
return typeof value === "string" && key.startsWith("api_");
});Searches for the first entry matching a callback predicate function in arrays or objects.
/**
* Find a specific entry in an array/object
* @param rootPath - Base dataPath from where to start searching
* @param callback - Method to filter the result and find the wanted entry. Receive the entry and its index.
* @returns Promise resolving to the first matching entry or undefined if none found
* @throws DataError if the path doesn't point to an array or object
*/
find<T>(rootPath: string, callback: FindCallback): Promise<T | undefined>;Usage Examples:
// Find user by email
const user = await db.find<User>("/users", (user, index) => {
return user.email === "alice@example.com";
});
// Find first post with specific tag
const jsPost = await db.find<Post>("/posts", (post, index) => {
return post.tags.includes("javascript");
});
// Find configuration by key
const dbConfig = await db.find<any>("/settings", (value, key) => {
return key === "database_url";
});
// Complex search with multiple conditions
const targetUser = await db.find<User>("/users", (user, index) => {
return user.age >= 18 && user.verified && user.role === "admin";
});Type definition for search callback functions.
/**
* Callback function for find/filter operations
* @param entry - The current entry being evaluated
* @param index - For arrays: numeric index, for objects: property name
* @returns Boolean indicating if entry matches search criteria
*/
type FindCallback = (entry: any, index: number | string) => boolean;Converts router-style paths to DataPath format for accessing nested array elements.
/**
* Convert a router style path to a normal path
* By default propertyName to search is "id"
* @param path - Router based path to convert (e.g., "/users/123/posts/456")
* @param propertyName - Name of the property to look for searchValue (default: "id")
* @returns Promise resolving to DataPath format
* @throws DataError if any part of the path is not found
*/
fromPath(path: string, propertyName?: string): Promise<string>;Usage Examples:
// Convert router path to DataPath
// Assumes users array with objects having "id" property
const dataPath = await db.fromPath("/users/123/posts/456");
// Result: "/users[2]/posts[1]" (if user with id 123 is at index 2, etc.)
// Use custom property name
const pathByEmail = await db.fromPath("/users/alice@example.com", "email");
// Result: "/users[0]" (if user with that email is at index 0)
// Use the converted path
const userData = await db.getData(dataPath);
// Multi-level conversion
const commentPath = await db.fromPath("/posts/1/comments/5/replies/2");
// Result: "/posts[0]/comments[4]/replies[1]"Router Path Format:
/collection/identifier/subcollection/identifier/collection[index]/subcollection[index]Node JSON DB includes built-in read/write locking for concurrent access safety. All JsonDB methods automatically use appropriate locks internally to ensure data integrity during concurrent operations.
Automatic Locking:
Concurrent Usage Example:
// Safe concurrent access - automatic locking handles this
const db = new JsonDB(new Config("shared.json"));
// Multiple processes/threads can safely access the same database
await Promise.all([
db.getData("/users/1"), // Read operation
db.getData("/users/2"), // Read operation
db.push("/users/3", data), // Write operation (will wait for reads)
db.count("/posts") // Read operation
]);// Basic array access
await db.getData("/users/0"); // First user
await db.getData("/users/1"); // Second user
// Nested array access
await db.getData("/posts/0/tags/1"); // Second tag of first post// Negative indexing (from end)
await db.getData("/users[-1]"); // Last user
await db.getData("/users[-2]"); // Second to last user
// Works with nested arrays
await db.getData("/posts/0/tags[-1]"); // Last tag of first post// Append to array using empty brackets
await db.push("/users[]", newUser);
// Append to nested array
await db.push("/posts/0/comments[]", newComment);
// Multi-dimensional append
await db.push("/matrix[]", newRow);
await db.push("/matrix[0][]", newValue);// Access 2D array
await db.getData("/matrix[0][1]");
// Append to 2D array
await db.push("/matrix[0][]", newValue);
// 3D array access
await db.getData("/cube[0][1][2]");// For multiple related updates, consider using merge mode
await db.push("/users/1", {
name: "Alice",
email: "alice@example.com",
updated: new Date()
}, false); // merge with existing data
// Or disable auto-save for multiple operations, then save manually
const db = new JsonDB(new Config("database.json", false)); // saveOnPush = false
await db.push("/users/1/name", "Alice");
await db.push("/users/1/email", "alice@example.com");
await db.push("/users/1/updated", new Date());
await db.save(); // Save all changes at once// For large arrays, use filter/find instead of loading entire arrays
const activeUsers = await db.filter<User>("/users", user => user.active);
// Rather than:
const allUsers = await db.getData("/users");
const activeUsers = allUsers.filter(user => user.active);// Reload periodically for long-running processes
setInterval(async () => {
await db.reload();
}, 300000); // Every 5 minutes
// Use getObjectDefault to avoid exceptions
const settings = await db.getObjectDefault("/settings", defaultSettings);