file-json-db

Real-World Examples

The Quick Start showed the basic API. This page walks through complete, realistic applications you can adapt for your own projects.

1. Issue / task tracker

A small team tracker with users and issues, indexes, and status workflows.

const { JSONDB } = require('file-json-db');
const path = require('path');

const db = new JSONDB(path.join(__dirname, 'data'));

const users = db.collection({
  name: 'users',
  autoId: true,
  indexes: ['email', 'username']
});

const issues = db.collection({
  name: 'issues',
  autoId: true,
  idType: 'uuid',
  indexes: ['status', 'assigneeId', 'priority']
});

// Seed team (run once)
if (users.find({}).count() === 0) {
  users.insertMany([
    { username: 'alice', email: 'alice@acme.dev', role: 'lead', active: true },
    { username: 'bob',   email: 'bob@acme.dev',   role: 'dev',  active: true },
    { username: 'carol', email: 'carol@acme.dev', role: 'qa',   active: true }
  ]);
}

const alice = users.findOne({ username: 'alice' });
const bob   = users.findOne({ username: 'bob' });

// Create an issue
const bug = issues.insert({
  title: 'Login crashes on Safari',
  description: 'TypeError when submitting the form on iOS Safari 17',
  status: 'open',
  priority: 'high',
  assigneeId: bob._id,
  reporterId: alice._id,
  labels: ['bug', 'frontend'],
  createdAt: new Date().toISOString()
});

// Move to in-progress
issues.updateOne(
  { _id: bug._id },
  {
    $set: {
      status: 'in-progress',
      updatedAt: new Date().toISOString()
    },
    $push: { labels: 'ios' }
  }
);

// Dashboard queries
const urgent = issues.find({
  status: { $in: ['open', 'in-progress'] },
  priority: 'high'
}).sort({ createdAt: -1 }).toArray();

const byStatus = issues.aggregate([
  { $group: { _id: '$status', count: { $sum: 1 } } }
]);

console.log('Urgent issues:', urgent.length);
console.log('By status:', byStatus);

2. Simple blog / CMS

const posts = db.collection({
  name: 'posts',
  autoId: true,
  indexes: ['slug', 'authorId', 'status']
});

const comments = db.collection({
  name: 'comments',
  autoId: true,
  indexes: ['postId']
});

// Publish a post
const post = posts.insert({
  title: 'Introducing file-json-db',
  slug: 'introducing-file-json-db',
  body: 'A resilient zero-dependency JSON database…',
  authorId: alice._id,
  status: 'published',
  tags: ['nodejs', 'database'],
  publishedAt: new Date().toISOString(),
  views: 0
});

// Increment view count
posts.updateOne(
  { slug: 'introducing-file-json-db' },
  { $inc: { views: 1 } }
);

// Add a comment
comments.insert({
  postId: post._id,
  author: 'Reader',
  body: 'Great article!',
  createdAt: new Date().toISOString()
});

// Load post + its comments
const article = posts.findOne({ slug: 'introducing-file-json-db' });
const articleComments = comments.find({ postId: article._id })
  .sort({ createdAt: 1 })
  .toArray();

3. Inventory / small e-commerce

const products = db.collection({
  name: 'products',
  autoId: true,
  indexes: ['sku', 'category']
});

const orders = db.collection({
  name: 'orders',
  autoId: true,
  idType: 'uuid',
  indexes: ['status', 'customerEmail']
});

// Stock a product
products.insert({
  sku: 'WIDGET-001',
  name: 'Wireless Widget',
  category: 'electronics',
  price: 29.99,
  stock: 100
});

// Place an order inside a transaction so stock and order stay consistent
async function placeOrder(sku, qty, customerEmail) {
  const product = products.findOne({ sku });
  if (!product || product.stock < qty) {
    throw new Error('Insufficient stock');
  }

  const txProducts = products.startTransaction();
  const txOrders   = orders.startTransaction(); // note: separate collections

  try {
    // In a real multi-collection transaction you would need external coordination.
    // Here we keep the critical stock update transactional on the products side.
    txProducts.updateOne(
      { sku },
      { $inc: { stock: -qty } }
    );

    const order = orders.insert({
      customerEmail,
      items: [{ sku, qty, price: product.price }],
      total: product.price * qty,
      status: 'pending',
      createdAt: new Date().toISOString()
    });

    await txProducts.commit();
    return order;
  } catch (err) {
    await txProducts.rollback();
    throw err;
  }
}
Note on multi-collection transactions Transactions are per-collection. For true multi-collection atomicity you need application-level compensation or a different database.

4. CLI configuration / local cache

// config-store.js – used by a CLI tool
const { JSONDB } = require('file-json-db');
const os = require('os');
const path = require('path');

const db = new JSONDB(path.join(os.homedir(), '.my-cli'));

const settings = db.collection({
  name: 'settings',
  autoId: false   // we use fixed keys
});

function getSetting(key, defaultValue) {
  const doc = settings.findOne({ key });
  return doc ? doc.value : defaultValue;
}

function setSetting(key, value) {
  const existing = settings.findOne({ key });
  if (existing) {
    settings.updateOne({ key }, { $set: { value, updatedAt: Date.now() } });
  } else {
    settings.insert({ key, value, updatedAt: Date.now() });
  }
}

module.exports = { getSetting, setSetting };

5. Electron main-process store

// main.js (Electron)
const { app } = require('electron');
const { JSONDB } = require('file-json-db');
const path = require('path');

const db = new JSONDB(path.join(app.getPath('userData'), 'db'));

const windows = db.collection({
  name: 'windowState',
  autoId: false
});

function saveWindowState(bounds) {
  const existing = windows.findOne({ id: 'main' });
  if (existing) {
    windows.updateOne({ id: 'main' }, { $set: { bounds } });
  } else {
    windows.insert({ id: 'main', bounds });
  }
}

function loadWindowState() {
  const doc = windows.findOne({ id: 'main' });
  return doc ? doc.bounds : { width: 1200, height: 800 };
}

6. Express REST API sketch

const express = require('express');
const { JSONDB } = require('file-json-db');

const app = express();
app.use(express.json());

const db = new JSONDB('./data');
const users = db.collection({
  name: 'users',
  autoId: true,
  indexes: ['email']
});

app.get('/users', async (req, res) => {
  const list = (await users.findAsync({}))
    .project({ passwordHash: 0 })
    .toArray();
  res.json(list);
});

app.post('/users', async (req, res) => {
  const existing = await users.findOneAsync({ email: req.body.email });
  if (existing) return res.status(409).json({ error: 'Email taken' });

  const user = await users.insertAsync({
    ...req.body,
    createdAt: new Date().toISOString()
  });
  res.status(201).json(user);
});

app.patch('/users/:id', async (req, res) => {
  const updated = await users.updateOneAsync(
    { _id: Number(req.params.id) },
    { $set: req.body }
  );
  if (!updated) return res.status(404).end();
  res.json(updated);
});

app.listen(3000);