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 🙏

© 2026 – Pkg Stats / Ryan Hefner

liekodb

v0.1.7

Published

Lightweight, MongoDB-like JSON database for Node.js

Readme

LiekoDB Documentation

Online documentation LiekoDB Documentation

Table of Contents

  1. Introduction
  2. Installation
  3. Quick Start
  4. Core Concepts
  5. Collection Methods
  6. Query Filters
  7. Examples
  8. Advanced Usage
  9. API Reference
  10. Best Practices

Introduction

LiekoDB is a lightweight, file-based database for Node.js with an optional HTTP adapter for browser and remote usage. It provides MongoDB-like query syntax with local file persistence and automatic caching.

Key Features

  • 🚀 Zero-dependency for local usage
  • 📁 File-based persistence with automatic caching
  • 🔄 MongoDB-like query syntax
  • 🌐 HTTP adapter for browser/remote usage
  • Auto-save with configurable intervals
  • 🔒 Collection-level locking for concurrency
  • 📊 Built-in logging and metrics

Installation

npm install liekodb

Quick Start

Basic Setup

// database.js
const LiekoDB = require('liekodb');

// Create a database instance
const db = new LiekoDB({
    debug: true,              // Enable debug logging
    autoSaveInterval: 10000   // Auto-save every 10 seconds
});

// Export for use in other files
module.exports = { db };

Your First Collection

// userService.js
const { db } = require('./database');

class UserService {
    constructor() {
        this.collection = db.collection('users');
    }

    async createUser(userData) {
        const { error, data } = await this.collection.insert({
            name: userData.name,
            email: userData.email,
            age: userData.age,
            createdAt: new Date().toISOString()
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async findUserByEmail(email) {
        const { error, data } = await this.collection.findOne({ email });
        if (error) throw new Error(error.message);
        return data;
    }
}

module.exports = new UserService();

Core Concepts

1. Database Instance

Create a database instance with optional configuration:

const db = new LiekoDB({
    debug: true,                    // Enable console logging
    autoSaveInterval: 5000,         // Auto-save interval in ms (0 to disable)
    storagePath: './mydata',        // Custom storage directory
    token: 'your-token-here',       // For HTTP adapter (browser/remote)
    databaseUrl: 'http://api.example.com' // For remote database
});

2. Collections

Collections are similar to tables in SQL or collections in MongoDB:

// Get or create a collection
const users = db.collection('users');
const products = db.collection('products');
const orders = db.collection('orders');

3. Documents

Documents are JSON objects stored in collections:

const userDocument = {
    id: 'abc123',              // Unique identifier (auto-generated if not provided)
    name: 'John Doe',
    email: '[email protected]',
    age: 30,
    tags: ['admin', 'premium'],
    settings: {
        notifications: true,
        theme: 'dark'
    },
    createdAt: '2024-01-15T10:30:00Z',
    updatedAt: '2024-01-15T10:30:00Z'
};

Collection Methods

CRUD Operations

1. Insert Documents

// Insert a single document
const { error, data } = await collection.insert({
    name: 'Alice',
    email: '[email protected]',
    age: 25
});

// Insert multiple documents
const { error, data } = await collection.insert([
    { name: 'Bob', email: '[email protected]' },
    { name: 'Charlie', email: '[email protected]' }
]);

// Result: data = {
//   insertedCount: 2,
//   updatedCount: 0,
//   totalDocuments: 5,
//   insertedIds: ['abc123', 'def456']
// }

2. Find Documents

// Find all documents
const { error, data } = await collection.find();

// Find with filters
const { error, data } = await collection.find({
    age: { $gt: 18 }
});

// Find with pagination
const { error, data } = await collection.find({}, {
    limit: 10,
    skip: 20,
    sort: { createdAt: -1 } // -1 = descending, 1 = ascending
});

// Find one document
const { error, data } = await collection.findOne({ email: '[email protected]' });

// Find by ID
const { error, data } = await collection.findById('document-id-here');

3. Update Documents

// Update by ID
const { error, data } = await collection.updateById('doc-id', {
    name: 'Updated Name',
    age: 31
});

// Update multiple documents with filters
const { error, data } = await collection.update(
    { status: 'pending' },      // Filter
    { status: 'completed' },    // Update
    { returnType: 'count' }     // Options
);

// Using update operators
const { error, data } = await collection.updateById('doc-id', {
    $set: { status: 'active' },
    $inc: { loginCount: 1 },
    $push: { logs: 'User logged in' }
});

4. Delete Documents

// Delete by ID
const { error, data } = await collection.deleteById('doc-id');

// Delete with filters
const { error, data } = await collection.delete({
    status: 'inactive',
    lastLogin: { $lt: '2023-01-01' }
});

// Delete entire collection
const { error, data } = await collection.drop();

5. Count Documents

// Count all documents
const { error, data } = await collection.count();

// Count with filters
const { error, data } = await collection.count({
    status: 'active',
    age: { $gte: 18 }
});

Query Options

const options = {
    sort: { createdAt: -1, name: 1 },     // Sort by multiple fields
    limit: 50,                            // Limit results
    skip: 100,                            // Skip first 100 results
    page: 3,                              // Page number (requires limit)
    fields: { name: 1, email: 1 },        // Include only specific fields
    returnType: 'documents',              // 'count', 'ids', or 'documents'
    maxReturn: 1000                       // Maximum documents to return
};

const { error, data } = await collection.find({}, options);

Query Filters

LiekoDB supports MongoDB-like query syntax:

Comparison Operators

// Equality
{ age: 25 }
{ name: 'John' }
{ status: { $eq: 'active' } }

// Inequality
{ age: { $ne: 25 } }
{ status: { $ne: 'inactive' } }

// Greater than / Less than
{ age: { $gt: 18 } }        // Greater than
{ age: { $gte: 21 } }       // Greater than or equal
{ age: { $lt: 65 } }        // Less than
{ age: { $lte: 100 } }      // Less than or equal

// In / Not in
{ role: { $in: ['admin', 'moderator'] } }
{ status: { $nin: ['banned', 'suspended'] } }

// Exists
{ email: { $exists: true } }
{ middleName: { $exists: false } }

// Regular expressions
{ email: { $regex: /@gmail\.com$/ } }
{ name: { $regex: '^J', $options: 'i' } } // Case insensitive

// Modulo
{ age: { $mod: [2, 0] } } // Even numbers

Logical Operators

// AND (default)
{ age: { $gt: 18 }, status: 'active' }

// OR
{ $or: [{ status: 'active' }, { verified: true }] }

// AND with OR
{
    $and: [
        { age: { $gte: 18 } },
        { 
            $or: [
                { role: 'admin' },
                { premium: true }
            ]
        }
    ]
}

// NOT
{ status: { $not: { $eq: 'banned' } } }
{ age: { $not: { $lt: 18 } } }

Array Queries

// Match array element
{ tags: 'javascript' }                     // Array contains 'javascript'
{ tags: { $in: ['javascript', 'nodejs'] } } // Array contains any of these

// Array field queries
{ 'skills.level': { $gt: 3 } }             // Nested array field
{ 'comments.0.author': 'admin' }           // First element of array

Nested Field Queries

// Dot notation for nested objects
{ 'address.city': 'New York' }
{ 'settings.theme': 'dark' }
{ 'metrics.visits.count': { $gt: 100 } }

Examples

Example 1: Todo List Application

// todoService.js
const { db } = require('./database');

class TodoService {
    constructor() {
        this.collection = db.collection('todos');
    }

    async createTodo(userId, text) {
        const { error, data } = await this.collection.insert({
            userId,
            text,
            completed: false,
            createdAt: new Date().toISOString(),
            priority: 'medium'
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async getUserTodos(userId, options = {}) {
        const { error, data } = await this.collection.find(
            { userId },
            {
                sort: { createdAt: -1 },
                ...options
            }
        );

        if (error) throw new Error(error.message);
        return data.foundDocuments;
    }

    async completeTodo(todoId) {
        const { error, data } = await this.collection.updateById(todoId, {
            completed: true,
            completedAt: new Date().toISOString()
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async deleteCompletedTodos(userId) {
        const { error, data } = await this.collection.delete({
            userId,
            completed: true
        });

        if (error) throw new Error(error.message);
        return data.deletedCount;
    }

    async getTodoStats(userId) {
        const total = await this.collection.count({ userId });
        const completed = await this.collection.count({
            userId,
            completed: true
        });
        const pending = await this.collection.count({
            userId,
            completed: false
        });

        return { total, completed, pending };
    }
}

module.exports = new TodoService();

Example 2: Blog System

// blogService.js
const { db } = require('./database');

class BlogService {
    constructor() {
        this.posts = db.collection('posts');
        this.comments = db.collection('comments');
    }

    async createPost(authorId, title, content, tags = []) {
        const { error, data } = await this.posts.insert({
            authorId,
            title,
            content,
            tags,
            status: 'published',
            views: 0,
            likes: 0,
            createdAt: new Date().toISOString(),
            updatedAt: new Date().toISOString()
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async getPosts(options = {}) {
        const { error, data } = await this.posts.find(
            { status: 'published' },
            {
                sort: { createdAt: -1 },
                limit: options.limit || 20,
                skip: options.skip || 0,
                fields: {
                    title: 1,
                    excerpt: 1,
                    authorId: 1,
                    tags: 1,
                    createdAt: 1,
                    views: 1,
                    likes: 1
                }
            }
        );

        if (error) throw new Error(error.message);
        return data;
    }

    async incrementViews(postId) {
        const { error, data } = await this.posts.updateById(postId, {
            $inc: { views: 1 }
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async addComment(postId, userId, text) {
        const { error, data } = await this.comments.insert({
            postId,
            userId,
            text,
            createdAt: new Date().toISOString()
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async getPostComments(postId) {
        const { error, data } = await this.comments.find(
            { postId },
            { sort: { createdAt: -1 } }
        );

        if (error) throw new Error(error.message);
        return data.foundDocuments;
    }

    async searchPosts(query) {
        const { error, data } = await this.posts.find({
            $or: [
                { title: { $regex: query, $options: 'i' } },
                { content: { $regex: query, $options: 'i' } },
                { tags: query }
            ]
        });

        if (error) throw new Error(error.message);
        return data.foundDocuments;
    }
}

module.exports = new BlogService();

Example 3: E-commerce Product Catalog

// productService.js
const { db } = require('./database');

class ProductService {
    constructor() {
        this.products = db.collection('products');
        this.categories = db.collection('categories');
    }

    async addProduct(productData) {
        const { error, data } = await this.products.insert({
            ...productData,
            sku: this.generateSKU(),
            stock: productData.stock || 0,
            price: parseFloat(productData.price),
            rating: 0,
            reviewCount: 0,
            createdAt: new Date().toISOString(),
            updatedAt: new Date().toISOString()
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async getProducts(filters = {}, options = {}) {
        const query = { ...filters, active: true };
        
        const { error, data } = await this.products.find(query, {
            sort: options.sort || { createdAt: -1 },
            limit: options.limit || 50,
            skip: options.skip || 0,
            ...options
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async updateStock(productId, quantity) {
        const { error, data } = await this.products.updateById(productId, {
            $inc: { stock: quantity }
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async addReview(productId, rating, review) {
        // Update product rating (average calculation)
        const product = await this.products.findById(productId);
        
        const newRating = (
            (product.rating * product.reviewCount + rating) /
            (product.reviewCount + 1)
        ).toFixed(1);

        const { error, data } = await this.products.updateById(productId, {
            $inc: { reviewCount: 1 },
            $set: { rating: parseFloat(newRating) }
        });

        if (error) throw new Error(error.message);
        
        // Store individual review
        await db.collection('reviews').insert({
            productId,
            rating,
            review,
            createdAt: new Date().toISOString()
        });

        return data;
    }

    async getProductsByCategory(categoryId, options = {}) {
        const { error, data } = await this.products.find(
            { categoryId, active: true },
            {
                sort: { price: options.sortPrice ? 1 : -1 },
                limit: options.limit || 20,
                ...options
            }
        );

        if (error) throw new Error(error.message);
        return data.foundDocuments;
    }

    async searchProducts(searchTerm, filters = {}) {
        const query = {
            $and: [
                {
                    $or: [
                        { name: { $regex: searchTerm, $options: 'i' } },
                        { description: { $regex: searchTerm, $options: 'i' } },
                        { tags: searchTerm }
                    ]
                },
                { ...filters, active: true }
            ]
        };

        const { error, data } = await this.products.find(query);
        if (error) throw new Error(error.message);
        return data.foundDocuments;
    }

    generateSKU() {
        return 'SKU-' + Date.now().toString(36) + Math.random().toString(36).substr(2, 5).toUpperCase();
    }
}

module.exports = new ProductService();

Example 4: Real-time Chat Application

// chatService.js
const { db } = require('./database');

class ChatService {
    constructor() {
        this.messages = db.collection('messages');
        this.rooms = db.collection('chat_rooms');
        this.users = db.collection('chat_users');
    }

    async createRoom(name, creatorId, isPrivate = false) {
        const { error, data } = await this.rooms.insert({
            name,
            creatorId,
            isPrivate,
            members: [creatorId],
            createdAt: new Date().toISOString()
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async sendMessage(roomId, userId, text) {
        const { error, data } = await this.messages.insert({
            roomId,
            userId,
            text,
            timestamp: new Date().toISOString(),
            readBy: [userId]
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async getRoomMessages(roomId, options = {}) {
        const { error, data } = await this.messages.find(
            { roomId },
            {
                sort: { timestamp: -1 },
                limit: options.limit || 50,
                ...options
            }
        );

        if (error) throw new Error(error.message);
        return data.foundDocuments.reverse(); // Return oldest first
    }

    async markAsRead(messageId, userId) {
        const { error, data } = await this.messages.updateById(messageId, {
            $addToSet: { readBy: userId }
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async getUnreadCount(roomId, userId) {
        const { error, data } = await this.messages.count({
            roomId,
            readBy: { $nin: [userId] }
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async getUserRooms(userId) {
        const { error, data } = await this.rooms.find({
            members: userId
        });

        if (error) throw new Error(error.message);
        return data.foundDocuments;
    }

    async addMemberToRoom(roomId, userId) {
        const { error, data } = await this.rooms.updateById(roomId, {
            $addToSet: { members: userId }
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async searchMessages(roomId, searchTerm) {
        const { error, data } = await this.messages.find({
            roomId,
            text: { $regex: searchTerm, $options: 'i' }
        });

        if (error) throw new Error(error.message);
        return data.foundDocuments;
    }
}

module.exports = new ChatService();

Example 5: Analytics Tracking System

// analyticsService.js
const { db } = require('./database');

class AnalyticsService {
    constructor() {
        this.events = db.collection('analytics_events');
        this.sessions = db.collection('user_sessions');
    }

    async trackEvent(userId, eventType, properties = {}) {
        const { error, data } = await this.events.insert({
            userId: userId || 'anonymous',
            eventType,
            properties,
            timestamp: new Date().toISOString(),
            userAgent: properties.userAgent,
            ip: properties.ip,
            path: properties.path
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async startSession(userId, userAgent, ip) {
        const sessionId = require('crypto').randomBytes(16).toString('hex');
        
        const { error, data } = await this.sessions.insert({
            sessionId,
            userId,
            userAgent,
            ip,
            startTime: new Date().toISOString(),
            lastActivity: new Date().toISOString(),
            events: []
        });

        if (error) throw new Error(error.message);
        return sessionId;
    }

    async updateSessionActivity(sessionId) {
        const { error, data } = await this.sessions.update(
            { sessionId },
            { lastActivity: new Date().toISOString() }
        );

        if (error) throw new Error(error.message);
        return data;
    }

    async getEventStats(eventType, startDate, endDate) {
        const { error, data } = await this.events.count({
            eventType,
            timestamp: {
                $gte: startDate,
                $lte: endDate
            }
        });

        if (error) throw new Error(error.message);
        return data;
    }

    async getUserActivity(userId, days = 7) {
        const startDate = new Date();
        startDate.setDate(startDate.getDate() - days);

        const { error, data } = await this.events.find({
            userId,
            timestamp: { $gte: startDate.toISOString() }
        }, {
            sort: { timestamp: -1 },
            fields: {
                eventType: 1,
                timestamp: 1,
                'properties.path': 1
            }
        });

        if (error) throw new Error(error.message);
        return data.foundDocuments;
    }

    async getPopularPages(limit = 10) {
        // Group by path using multiple queries (simplified)
        const allEvents = await this.events.find({}, {
            fields: { 'properties.path': 1 },
            limit: 10000
        });

        const pathCounts = {};
        allEvents.foundDocuments.forEach(event => {
            const path = event.properties?.path;
            if (path) {
                pathCounts[path] = (pathCounts[path] || 0) + 1;
            }
        });

        return Object.entries(pathCounts)
            .sort(([,a], [,b]) => b - a)
            .slice(0, limit)
            .map(([path, count]) => ({ path, count }));
    }

    async cleanupOldData(daysToKeep = 90) {
        const cutoffDate = new Date();
        cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);

        // Delete old events
        const eventsResult = await this.events.delete({
            timestamp: { $lt: cutoffDate.toISOString() }
        });

        // Delete old sessions
        const sessionsResult = await this.sessions.delete({
            lastActivity: { $lt: cutoffDate.toISOString() }
        });

        return {
            deletedEvents: eventsResult.deletedCount || 0,
            deletedSessions: sessionsResult.deletedCount || 0
        };
    }
}

module.exports = new AnalyticsService();

Advanced Usage

Database Management

// Get database status
const status = await db.status();
console.log(status);
// {
//   storagePath: './storage',
//   collections: [...],
//   totalCollections: 5,
//   totalDocuments: 1234,
//   totalCollectionsSize: 1024576,
//   totalCollectionsSizeFormatted: '1.02 MB'
// }

// List all collections
const collections = await db.listCollections();
collections.forEach(col => {
    console.log(`${col.name}: ${col.totalDocuments} docs (${col.sizeFormatted})`);
});

// Drop a collection
await db.dropCollection('old_data');

// Close database (flushes all pending writes)
await db.close();

HTTP Adapter (Browser/Remote)

// Browser usage
const db = new LiekoDB({
    token: 'your-api-token',
    databaseUrl: 'https://api.yourservice.com'
});

// The API is the same as local usage
const users = db.collection('users');
const { data } = await users.find({ active: true });

Custom Storage Path

const db = new LiekoDB({
    storagePath: './myapp/data',
    autoSaveInterval: 30000 // Save every 30 seconds
});

// Or set via environment variable
const db = new LiekoDB({
    storagePath: process.env.DB_PATH || './storage'
});

Error Handling

async function safeDatabaseOperation() {
    try {
        const { error, data } = await collection.insert(document);
        
        if (error) {
            // Handle specific error types
            if (error.code === 404) {
                console.error('Collection not found');
            } else if (error.message.includes('validation')) {
                console.error('Validation error:', error.message);
            } else {
                console.error('Database error:', error);
            }
            return null;
        }
        
        return data;
    } catch (err) {
        console.error('Unexpected error:', err);
        throw err;
    }
}

Performance Optimization

const db = new LiekoDB({
    autoSaveInterval: 30000, // Longer interval for write-heavy apps
    debug: false // Disable debug in production
});

// Use projection to reduce data transfer
const { data } = await collection.find({}, {
    fields: { name: 1, email: 1 }, // Only get these fields
    limit: 100
});

// Use count instead of find when only need count
const { data: count } = await collection.count(filters);

// Use pagination for large datasets
async function getAllPaginated(collection, batchSize = 1000) {
    let allDocuments = [];
    let skip = 0;
    let hasMore = true;

    while (hasMore) {
        const { data } = await collection.find({}, {
            limit: batchSize,
            skip: skip
        });

        if (data.foundDocuments.length === 0) {
            hasMore = false;
        } else {
            allDocuments = allDocuments.concat(data.foundDocuments);
            skip += batchSize;
        }
    }

    return allDocuments;
}

API Reference

LiekoDB Constructor

new LiekoDB(options)

Options:

  • debug (boolean): Enable debug logging (default: false)
  • autoSaveInterval (number): Auto-save interval in ms (default: 5000)
  • storagePath (string): Path for file storage (default: './storage')
  • token (string): Authentication token for HTTP adapter
  • databaseUrl (string): URL for remote database
  • poolSize (number): HTTP connection pool size (default: 10)
  • maxRetries (number): HTTP retry attempts (default: 3)
  • timeout (number): HTTP timeout in ms (default: 15000)

Collection Methods

All methods return a Promise resolving to { error?, data? }

insert(documents)

Insert one or multiple documents.

find(filters?, options?)

Find documents matching filters.

findOne(filters?, options?)

Find a single document.

findById(id, options?)

Find document by ID.

update(filters, update, options?)

Update multiple documents.

updateById(id, update, options?)

Update document by ID.

delete(filters)

Delete documents matching filters.

deleteById(id)

Delete document by ID.

count(filters?)

Count documents matching filters.

drop()

Delete entire collection.

Query Operators

Comparison

  • $eq - Equal
  • $ne - Not equal
  • $gt - Greater than
  • $gte - Greater than or equal
  • $lt - Less than
  • $lte - Less than or equal
  • $in - In array
  • $nin - Not in array
  • $exists - Field exists
  • $regex - Regular expression
  • $mod - Modulo operation

Logical

  • $and - Logical AND
  • $or - Logical OR
  • $not - Logical NOT
  • $nor - Logical NOR

Update Operators

  • $set - Set field value
  • $unset - Remove field
  • $inc - Increment field
  • $push - Append to array
  • $pull - Remove from array
  • $addToSet - Add to array if not exists

Best Practices

1. Always Handle Errors

async function safeOperation() {
    const { error, data } = await collection.operation();
    if (error) {
        // Handle appropriately
        throw new Error(`Database error: ${error.message}`);
    }
    return data;
}

2. Use Indexes Wisely

While LiekoDB doesn't have traditional indexes, you can:

  • Keep collections small and focused
  • Use meaningful IDs for quick lookups
  • Consider splitting large collections

3. Implement Data Validation

class UserService {
    async createUser(userData) {
        // Validate before inserting
        if (!userData.email || !userData.email.includes('@')) {
            throw new Error('Invalid email');
        }
        
        const { error, data } = await this.collection.insert({
            ...userData,
            createdAt: new Date().toISOString(),
            updatedAt: new Date().toISOString()
        });
        
        return data;
    }
}

4. Optimize for Your Use Case

// For read-heavy: Larger auto-save interval
const readHeavyDB = new LiekoDB({
    autoSaveInterval: 60000 // 1 minute
});

// For write-heavy: Smaller auto-save interval
const writeHeavyDB = new LiekoDB({
    autoSaveInterval: 1000 // 1 second
});

5. Backup Strategy

// Simple backup function
async function backupDatabase(sourcePath, backupPath) {
    const fs = require('fs').promises;
    
    try {
        const files = await fs.readdir(sourcePath);
        
        for (const file of files) {
            if (file.endsWith('.json')) {
                const source = `${sourcePath}/${file}`;
                const backup = `${backupPath}/${file}.${Date.now()}.bak`;
                await fs.copyFile(source, backup);
            }
        }
        
        console.log('Backup completed successfully');
    } catch (error) {
        console.error('Backup failed:', error);
    }
}

6. Monitor Performance

// Add performance logging
const db = new LiekoDB({
    debug: process.env.NODE_ENV === 'development'
});

// Log slow queries
const start = Date.now();
const { data } = await collection.find(complexQuery);
const duration = Date.now() - start;

if (duration > 1000) { // 1 second
    console.warn(`Slow query: ${duration}ms`);
}

Troubleshooting

Common Issues

  1. "Collection name contains invalid characters"

    • Use only alphanumeric characters, underscores, and hyphens
    • Cannot start with numbers
  2. Data not persisting

    • Check autoSaveInterval setting
    • Call db.close() before exiting
    • Check file permissions
  3. Slow queries

    • Use projection to limit returned fields
    • Implement pagination
    • Consider splitting large collections
  4. Memory usage

    • Monitor cache size for large collections
    • Use limit in queries
    • Consider disabling auto-save for read-only operations

Debug Mode

Enable debug mode to see detailed logs:

const db = new LiekoDB({ debug: true });

This will log:

  • All database operations
  • Execution times
  • Response sizes
  • Errors and warnings

Conclusion

LiekoDB provides a simple yet powerful solution for local data persistence in Node.js applications. With its MongoDB-like API, automatic persistence, and flexible adapters, it's suitable for a wide range of applications from small projects to production systems.

Remember to:

  • Always handle errors properly
  • Implement appropriate data validation
  • Choose the right adapter for your environment
  • Monitor performance and implement backups

For more examples and advanced usage, check the source code and experiment with different configurations to find what works best for your specific use case.