CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-lowdb

Tiny local JSON database for Node, Electron and the browser

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

node.mddocs/

Node.js File Storage

File-based storage adapters and convenience presets for Node.js environments. These adapters support JSON files, plain text files, and custom data formats with atomic write operations for data safety.

Capabilities

JSON File Presets

Pre-configured database instances with JSON file adapters for immediate use.

/**
 * Create a Low instance with JSONFile adapter, automatically switches to Memory adapter during tests
 * @param filename - Path to the JSON file
 * @param defaultData - Default data structure
 * @returns Promise resolving to configured Low instance with data pre-loaded
 */
function JSONFilePreset<Data>(filename: PathLike, defaultData: Data): Promise<Low<Data>>;

/**
 * Create a LowSync instance with JSONFileSync adapter, automatically switches to MemorySync adapter during tests
 * @param filename - Path to the JSON file
 * @param defaultData - Default data structure
 * @returns Configured LowSync instance with data pre-loaded
 */
function JSONFileSyncPreset<Data>(filename: PathLike, defaultData: Data): LowSync<Data>;

Usage Examples:

import { JSONFilePreset, JSONFileSyncPreset } from "lowdb/node";

// Async preset
type Data = { posts: Array<{ id: number; title: string }> };
const defaultData: Data = { posts: [] };

const db = await JSONFilePreset("db.json", defaultData);
await db.update(({ posts }) => posts.push({ id: 1, title: "Hello World" }));

// Sync preset
const syncDb = JSONFileSyncPreset("sync-db.json", { settings: {} });
syncDb.update((data) => {
  data.settings.theme = "dark";
});

JSON File Adapters

Direct JSON file adapters with automatic parsing and formatting.

/**
 * Async JSON file adapter with automatic JSON parsing/stringifying
 * @template T - The type of data stored in the JSON file
 */
class JSONFile<T> implements Adapter<T> {
  constructor(filename: PathLike);
  
  /** Read and parse JSON file content, returns null if file doesn't exist */
  read(): Promise<T | null>;
  
  /** Stringify and write data to JSON file with atomic write operation */
  write(obj: T): Promise<void>;
}

/**
 * Sync JSON file adapter with automatic JSON parsing/stringifying
 * @template T - The type of data stored in the JSON file
 */
class JSONFileSync<T> implements SyncAdapter<T> {
  constructor(filename: PathLike);
  
  /** Read and parse JSON file content synchronously, returns null if file doesn't exist */
  read(): T | null;
  
  /** Stringify and write data to JSON file synchronously with atomic write operation */
  write(obj: T): void;
}

Usage Examples:

import { Low, LowSync } from "lowdb";
import { JSONFile, JSONFileSync } from "lowdb/node";

// Async JSON file adapter
const adapter = new JSONFile("data.json");
const db = new Low(adapter, { users: [] });
await db.read();
db.data.users.push({ id: 1, name: "Alice" });
await db.write();

// Sync JSON file adapter
const syncAdapter = new JSONFileSync("config.json");
const syncDb = new LowSync(syncAdapter, { theme: "light" });
syncDb.read();
syncDb.data.theme = "dark";
syncDb.write();

Text File Adapters

Raw text file adapters for custom data formats and plain text storage.

/**
 * Async text file adapter using steno for atomic writes
 */
class TextFile implements Adapter<string> {
  constructor(filename: PathLike);
  
  /** Read text file content, returns null if file doesn't exist */
  read(): Promise<string | null>;
  
  /** Write text to file with atomic write operation using steno */
  write(str: string): Promise<void>;
}

/**
 * Sync text file adapter with atomic writes using temporary files
 */
class TextFileSync implements SyncAdapter<string> {
  constructor(filename: PathLike);
  
  /** Read text file content synchronously, returns null if file doesn't exist */
  read(): string | null;
  
  /** Write text to file synchronously with atomic write using temporary file */
  write(str: string): void;
}

Usage Examples:

import { Low, LowSync } from "lowdb";
import { TextFile, TextFileSync } from "lowdb/node";

// Async text file adapter
const textAdapter = new TextFile("log.txt");
const logDb = new Low(textAdapter, "");
await logDb.read();
await logDb.update((data) => data + "New log entry\n");

// Sync text file adapter
const syncTextAdapter = new TextFileSync("notes.txt");
const notesDb = new LowSync(syncTextAdapter, "");
notesDb.read();
notesDb.data += "Important note\n";
notesDb.write();

Data File Adapters

Flexible adapters for custom data formats with configurable parse and stringify functions.

/**
 * Async data file adapter with custom parse/stringify functions
 * @template T - The type of data stored
 */
class DataFile<T> implements Adapter<T> {
  constructor(
    filename: PathLike,
    options: {
      parse: (str: string) => T;
      stringify: (data: T) => string;
    }
  );
  
  /** Read file content and parse using custom parse function */
  read(): Promise<T | null>;
  
  /** Stringify data using custom stringify function and write to file */
  write(obj: T): Promise<void>;
}

/**
 * Sync data file adapter with custom parse/stringify functions
 * @template T - The type of data stored
 */
class DataFileSync<T> implements SyncAdapter<T> {
  constructor(
    filename: PathLike,
    options: {
      parse: (str: string) => T;
      stringify: (data: T) => string;
    }
  );
  
  /** Read file content and parse using custom parse function synchronously */
  read(): T | null;
  
  /** Stringify data using custom stringify function and write to file synchronously */
  write(obj: T): void;
}

Usage Examples:

import { Low } from "lowdb";
import { DataFile } from "lowdb/node";
import YAML from "yaml";

// YAML file adapter
const yamlAdapter = new DataFile("config.yaml", {
  parse: YAML.parse,
  stringify: YAML.stringify
});

const yamlDb = new Low(yamlAdapter, { servers: [] });
await yamlDb.read();

// CSV file adapter with custom parsing
const csvAdapter = new DataFile("data.csv", {
  parse: (str) => str.split('\n').map(line => line.split(',')),
  stringify: (data) => data.map(row => row.join(',')).join('\n')
});

// Encrypted JSON adapter
const encryptedAdapter = new DataFile("secret.json", {
  parse: (str) => JSON.parse(decrypt(str)),
  stringify: (data) => encrypt(JSON.stringify(data))
});

File System Behavior

Atomic Writes

All file adapters use atomic write operations to prevent data corruption:

  • TextFile: Uses the steno library for atomic writes with temporary files
  • TextFileSync: Creates temporary files and uses fs.renameSync for atomic operations
  • JSONFile/JSONFileSync: Inherit atomic behavior from TextFile adapters
  • DataFile/DataFileSync: Inherit atomic behavior from TextFile adapters

File Not Found Handling

When a file doesn't exist:

const db = await JSONFilePreset("nonexistent.json", { count: 0 });
// db.data will be { count: 0 } (default data)

await db.read(); // No error thrown, uses default data
// db.data remains { count: 0 }

await db.write(); // Creates the file with current data

Directory Creation

File adapters do not automatically create parent directories. Ensure directories exist:

import { mkdir } from "fs/promises";
import { JSONFilePreset } from "lowdb/node";

// Create directory if needed
await mkdir("data", { recursive: true });
const db = await JSONFilePreset("data/app.json", {});

Test Environment Behavior

Presets automatically switch to memory adapters when NODE_ENV=test:

// In test environment (NODE_ENV=test)
const db = await JSONFilePreset("db.json", { posts: [] });
// Uses Memory adapter instead of JSONFile
// No actual file I/O occurs during testing

// In production environment
const db = await JSONFilePreset("db.json", { posts: [] });
// Uses JSONFile adapter for actual file persistence

Error Handling

File adapters may throw various Node.js file system errors:

import { JSONFilePreset } from "lowdb/node";

try {
  const db = await JSONFilePreset("/readonly/db.json", {});
  await db.write();
} catch (error) {
  if (error.code === "EACCES") {
    console.error("Permission denied");
  } else if (error.code === "ENOENT") {
    console.error("Directory doesn't exist");
  } else if (error.code === "ENOSPC") {
    console.error("No space left on device");
  }
}

// Handle JSON parsing errors
try {
  const db = await JSONFilePreset("malformed.json", {});
  await db.read();
} catch (error) {
  if (error instanceof SyntaxError) {
    console.error("Invalid JSON format");
  }
}

Performance Considerations

Sync vs Async

  • Async adapters: Non-blocking I/O, suitable for servers and applications with concurrent operations
  • Sync adapters: Blocking I/O, suitable for CLI tools and simple scripts

File Size

lowdb loads the entire file into memory. For large datasets (>100MB), consider:

  • Splitting data across multiple files
  • Using streaming database solutions
  • Implementing custom adapters with partial loading

Write Frequency

Atomic writes create temporary files. For high-frequency writes:

  • Batch updates using the update() method
  • Consider using Memory adapters with periodic persistence
  • Implement custom adapters with write buffering

Types

/** Node.js path-like type (string, Buffer, or URL) */
type PathLike = string | Buffer | URL;

docs

browser.md

core.md

index.md

node.md

tile.json