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

vladikdb

v3.0.0

Published

Simple JSON database for node and browser

Readme

vladikdb

Simple JSON database for node and browser

This is an alternative to lowdb.

If you know JavaScript, you know how to use vladikdb.

Features:

  • Simple
  • Fast due to indexing
  • Works in node and browser
  • Safe atomic file writing (single-threaded only)
  • Data storage in any format and location (JSON, YAML, HTTP, ...)
  • Works with any data structures (Collection, Single, Graph, ...)

Quick Start

Installation:

npm install vladikdb

Usage:

import path from 'node:path'

// import { LocalStorage } from 'vladikdb/browser'
import VladikDB, { Collection } from 'vladikdb'
import { JSONFile } from 'vladikdb/node'

interface Post {
  id: number
  userId: number
  title: string
}

// Node
const databasePostsPath = path.join('database', 'posts.json')
const adapter = new JSONFile<Post[]>(databasePostsPath)

// Browser
// const databasePostsKey = 'posts'
// const adapter = new LocalStorage<Post[]>(databasePostsKey)

const database = new VladikDB({
  posts: new Collection(adapter, 'id', ['userId']),
})

// Read
await database.read()

// Create
database.content.posts.create({
  id: 1,
  userId: 2,
  title: 'vladikdb is awesome',
})

// Read by userId
const post1 = database.content.posts.findByIndexedField('userId', 2)
console.log(post1)

// Read by id
const post2 = database.content.posts.findByPrimaryKey(1)
console.log(post2)

// Update
database.content.posts.updateByPrimaryKey(1, {
  id: 1,
  userId: 2,
  title: 'new title',
})

// Delete
database.content.posts.deleteByPrimaryKey(1)

// Get documents
const posts = database.content.posts.getDocuments()

// Change documents
const newPosts = [...posts].map((post) => {
  return {
    ...post,
    title: 'changed title',
  }
})

// Set documents
database.content.posts.setDocuments(newPosts)

// Write posts
await database.content.posts.write()
// Write all database
await database.write()

Content Types

The database provides two built-in content types:

  • Collection (object[]) - a collection of documents designed for fast operations with documents by primary keys and fast search by indexed fields.
  • Single (object) - designed for single objects, such as application configuration.

Example of using Single:

import path from 'node:path'

// import { LocalStorage } from 'vladikdb/browser'
import VladikDB, { Single } from 'vladikdb'
import { JSONFile } from 'vladikdb/node'

interface Config {
  apiKey?: string
  loglevel?: string
}

// Node
const databasePath = 'database'
const databaseConfigPath = path.join(databasePath, 'config.json')
const adapter = new JSONFile<Config>(databaseConfigPath)

// Browser
// const databaseConfigKey = 'config'
// const adapter = new LocalStorage<Config>(databaseConfigKey)

const database = new VladikDB({
  config: new Single<Config>(adapter, {}),
})

await database.read()

database.content.config.setData({
  apiKey: '<NEW_API_KEY>',
})

const data = database.content.config.getData()
console.log(data)

await database.write()

Creating a Custom Content Type

You can create a new content type for optimal, fast operations with any data structure.

To create a content type, you need to implement the Content interface:

interface Content<T> {
  adapter: Adapter<T>
  read: () => Promise<void>
  write: (force?: boolean) => Promise<void>
}

As an example, refer to the source code of built-in content types: https://github.com/vladpuz/vladikdb/tree/main/src/content.

Adapters

List of Built-in Adapters

For node:

  • TextFile
  • DataFile
  • JSONFile

For browser:

  • WebStorage
  • SessionStorage
  • LocalStorage

For any environment:

  • Memory

Creating a Custom Adapter

You can create a new adapter for storing data in any format and location, such as YAML, remote storage, data encryption, etc.

To create an adapter, you need to implement the Adapter interface:

interface Adapter<T> {
  read: () => Promise<T | null> | (T | null)
  write: (data: T) => Promise<void> | void
}

As an example, refer to the source code of built-in adapters: https://github.com/vladpuz/vladikdb/tree/main/src/adapters.

Primary Key Generation

In the node environment:

import crypto from 'node:crypto'

const uuid = crypto.randomUUID()

database.content.posts.create({
  id: uuid,
  title: 'vladikdb is awesome',
})

In the browser environment:

const uuid = crypto.randomUUID()

database.content.posts.create({
  id: uuid,
  title: 'vladikdb is awesome',
})

Optimization

When working with a large amount of data, you will face performance issues. This happens because each call to write() serializes data through JSON.stringify, thus even if only one document is changed, the JSON format must convert all documents to a string before writing.

This can be mitigated by accumulating changes and performing write() at intervals and before exiting the application to avoid data loss:

const WRITE_INTERVAL = 60 * 1000

const intervalId = setInterval(() => {
  database.write()
}, WRITE_INTERVAL)

// Node (Docker, pm2, ...)
process.on('SIGINT', () => {
  clearInterval(intervalId)
  database.write()
})
process.on('SIGTERM', () => {
  clearInterval(intervalId)
  database.write()
})

// Browser
window.addEventListener('beforeunload', () => {
  clearInterval(intervalId)
  database.write()
})

By default, the database content checks for changes before writing. If there are no changes, the write operation does not occur. This means you can call database.write() at intervals without worrying about unnecessary data writes.

Comparison with lowdb

  • lowdb and vladikdb use steno for safe atomic file writing (single-threaded only).
  • vladikdb introduces a new entity Content, which defines the structure of stored data and provides methods for efficient work with this data structure (indexing, etc.). lowdb does not handle efficient data operations, delegating this responsibility to the user.
  • lowdb provides both synchronous and asynchronous adapters and database instances, while vladikdb provides both synchronous and asynchronous adapters, but Content and the database are always asynchronous.
  • The built-in TextFile adapter from vladikdb recursively creates the directory if it does not exist, whereas the lowdb adapter will throw an error.
  • The built-in JSONFile adapter from vladikdb allows setting any JSON space (indent), while lowdb always uses space 2.
  • lowdb adapters are compatible with vladikdb adapters, and vladikdb has the same set of built-in adapters as lowdb.

API

VladikDB

new VladikDB(content)

content: ContentObject

Creates a database instance for managing content.

database.content

Type: ContentObject

The content object passed when creating the instance.

database.contentArray

Type: Content[]

The array of content passed when creating the instance.

database.read()

Calls read() for all content.

database.write(force?)

  • force? (boolean = false) - Forces writing even if there are no data changes.

Calls write() for all content.

Collection

new Collection(adapter, primaryKeyField, indexedFields?)

Creates a collection instance.

  • adapter (Adapter) - Any adapter.
  • primaryKeyField (keyof Document) - The field of the document used as the primary key. The specified field must contain only primitive data types.
  • indexedFields? ((keyof Document)[]) - Indexed fields of the document. Should not include primaryKeyField.

collection.adapter

Type: Adapter

The adapter passed when creating the instance.

collection.primaryKeyField

Type: keyof Document

The primary key field passed when creating the instance.

collection.indexedFields

Type: (keyof Document)[]

The indexed fields passed when creating the instance.

collection.read()

Complexity: O(n)

Reads data through the collection adapter.

collection.write(force?)

Complexity: O(n)

  • force? (boolean = false) - Forces writing even if there are no data changes.

Writes data through the collection adapter.

collection.getDocuments()

Complexity: O(1)

Return: Document[]

Gets the collection documents.

collection.setDocuments(documents)

Complexity: O(n)

documents: Document[]

Sets the collection documents.

collection.create(document)

Complexity: O(1)

document: Document

Creates a document.

Throws an error if a document with the same primary key already exists.

collection.findByIndexedField(field, value):

Complexity: O(1)

Return: Document | undefined

field: keyof Document

value: Document[keyof Document]

Searches for a document by an indexed field.

Throws an error if the parameter field was not specified in indexedFields when creating the instance.

collection.findByPrimaryKey(primaryKey)

Complexity: O(1)

Return: Document | undefined

primaryKey: keyof Document

Searches for a document by primary key.

collection.updateByPrimaryKey(primaryKey, document)

Complexity: O(1)

primaryKey: keyof Document

document: Document

Updates a document by primary key.

Throws an error if a document with the primary key primaryKey does not exist.

Throws an error when attempting to update the document's primary key. Instead, delete the old document and create a new one.

collection.deleteByPrimaryKey(primaryKey)

Complexity: O(1)

primaryKey: keyof Document

Deletes documents by primary key.

Throws an error if a document with the primary key primaryKey does not exist.

Single

new Single(adapter, defaultData)

Creates a single instance.

  • adapter (Adapter) - Any adapter.
  • defaultData (Data) - Default data.

single.adapter

Type: Adapter

The adapter passed when creating the instance.

single.defaultData

Type: Data

The default data passed when creating the instance.

single.read()

Reads data through the single adapter.

single.write(force?)

  • force? (boolean = false) - Forces writing even if there are no data changes.

Writes data through the single adapter.

single.getData()

Return: Data

Gets the single data.

single.setData(data)

data: Data

Sets the single data.