# lowdb: a JSON file as your database, for Node, Electron and the browser

> lowdb stores your data in a plain JSON file you can read and edit by hand. It fits small Node scripts, Electron apps and prototypes, and it stops being the right choice the moment two processes need to write at once.

**typicode/lowdb** — Simple and fast JSON database

- Repository: https://github.com/typicode/lowdb
- Stars: 22,585 · Forks: 963
- Language: JavaScript
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/typicode-lowdb

## The problem lowdb solves: persistence without a server

Most small programs need to remember something between runs. A settings file, a list of posts, a cache of API responses. The usual answers are a database server you now have to install and operate, or hand-rolled fs.readFile and fs.writeFile calls that lose data when two writes overlap. lowdb sits between those: it is an npm package that reads and writes a JSON file, and the data lives in memory as a normal JavaScript object while your program runs.

The intended audience is narrow and clear. Node scripts and CLI tools, Electron desktop apps, browser pages that want structured storage on top of localStorage or sessionStorage, and test suites that want to swap a real file for memory. The README's own framing is the giveaway: "If you know JavaScript, you know how to use lowdb." There is no query language, no schema migration system and no server process. If your data model is an object you would happily write to disk by hand, lowdb is the right size.

## How lowdb works: adapters, db.data and atomic writes

The architecture is two layers. A Low or LowSync instance holds defaultData and exposes db.data, db.read(), db.write() and db.update(fn). An adapter below it knows how to move that data to and from a medium. JSONFile and JSONFileSync handle files; Memory and MemorySync skip persistence for tests; LocalStorage and SessionStorage target the browser; TextFile and DataFile exist so you can build your own format, for example YAML via a custom parse and stringify pair.

The data flow is deliberately boring. db.read() calls adapter.read() and assigns the result to db.data. You mutate db.data with ordinary JavaScript, then call db.write(), which calls adapter.write(db.data). db.update(fn) runs your function and then writes, which is the one-call form the README shows. Queries are native Array methods on db.data, so posts.find((post) => post.id === 1) is the whole query API.

Two details matter more than they look. First, the README notes that JSONFile and JSONFileSync set db.data to null when the file does not exist, so a read before any write leaves you with null rather than your default. Second, the feature list claims "safe atomic writes", and package.json shows a single runtime dependency, steno, which is the library doing that work. Atomic here means the write goes through a temporary file and a rename, so a crash mid-write does not leave a half-written db.json. It does not mean concurrent writers are coordinated; there is no lock across processes.

## Installing lowdb and writing your first record

Installation is one npm command. The package is pure ESM, so the README points readers to a separate explainer if their project is still CommonJS; on modern Node this is a non-issue, on an older CommonJS codebase it is the first thing that will break.

```bash
npm install lowdb
```

The fastest start is the JSONFilePreset, which creates the file if it is missing and gives you a ready Low instance. The README's own example is this:

```js
import { JSONFilePreset } from 'lowdb/node'

const defaultData = { posts: [] }
const db = await JSONFilePreset('db.json', defaultData)

await db.update(({ posts }) => posts.push('hello world'))
```

After that runs, db.json on disk contains an object with a posts array holding the string "hello world". If you prefer to control when the file is touched, mutate db.data and call db.write() yourself.

TypeScript users get compile-time checking by declaring the shape once and passing it as the default. The README shows the failure mode explicitly: pushing a string into a string array type-checks, pushing a number does not.

```ts
type Data = {
  messages: string[]
}

const defaultData: Data = { messages: [] }
const db = await JSONPreset<Data>('db.json', defaultData)

db.data.messages.push('foo')
```

Note the README writes JSONPreset here while the presets list names JSONFilePreset; the file-based preset is the one documented in the usage section, and it is what you should reach for.

## What lowdb is not: concurrency, scale and query limits

The honest limitation is that db.data is the entire dataset in memory, and every db.write() serialises all of it back to the file. A few thousand records is fine. A hundred thousand records means your process holds them all, and each write rewrites the whole file. There is no partial update, no index and no cursor.

The second limitation is concurrency. Atomic writes protect against a torn file, not against two writers. Two Node processes, or a Node process and an Electron renderer, each holding their own Low instance over the same path will overwrite each other's changes on the last write. The README does not document any locking, and nothing in the API suggests one exists. If you need more than one writer, that is the signal to move to a server-backed database.

The third is that there is no query engine. Filtering a million rows with Array.prototype.filter is a full scan in JavaScript, which is fine for the datasets lowdb targets and wrong for anything larger. The fourth is durability granularity: you decide when to write. Crash between an in-memory mutation and db.write() and the change is gone. That is a design choice, not a bug, but it is worth stating plainly because the README's framing is all convenience.

## lowdb vs SQLite, MongoDB and a plain JSON file

The comparison people actually search for is lowdb vs SQLite. Both are embedded and serverless, so the difference is the data model and the guarantees. SQLite is a relational engine with a real query planner, transactions, indexes and a stable on-disk format; it ships as a native binding or a WASM build, which is the cost you pay. lowdb is a JavaScript object plus a JSON file, so the cost is that you write your own query logic and accept whole-file rewrites. If your access pattern is "load everything, change one thing, save everything", lowdb is simpler. If it is "find the rows matching a condition among many", SQLite wins on both speed and code volume.

Against MongoDB the split is even wider. MongoDB is a server, or at least a separate process, with network calls, authentication and an operational surface. You choose it when data is shared across machines or exceeds memory. lowdb has none of that surface, which is exactly why it is pleasant for a desktop app's settings file and hopeless as a shared backend.

The third alternative is doing it yourself with fs. That is genuinely viable for a single small file, and lowdb's value over it is the atomic write from steno plus the read/write/update API. If you already have a careful write-temp-then-rename helper, lowdb's advantage shrinks to ergonomics.

## Maintenance, upgrades and the MIT licence

The repository is not archived. The last push was on 2026-03-27, so code has moved since the v7.0.0 release on 2023-12-26, but there has been no tagged release in the intervening period. package.json reports version 7.0.1, which is ahead of the latest published release listed. In practice that means fixes may exist on main that you cannot install from npm under a version number, so pinning to a release and reading the commit log before upgrading is the sensible posture.

Upgrade cost is low by construction. The public surface is a handful of presets, two classes, a few adapters and four methods. The one migration that bites is the ESM-only packaging introduced with v7: CommonJS consumers need a dynamic import or a bundler. There is no changelog file at the top level of the repository, so the release notes on GitHub are the place to check before moving between majors.

The licence is MIT, which permits commercial and closed-source use provided the copyright notice and permission notice are retained. That is a factual statement about the licence text, not legal advice; if your organisation has a policy on bundled dependencies, the single runtime dependency, steno, needs to be checked too.

## Conclusion

Adopt lowdb when one process owns the file and the data fits comfortably in memory: CLI tools, Electron settings, test fixtures, small internal services. Do not adopt it when several processes or machines write the same data, when you need queries across large collections, or when a lost write is unacceptable. Before committing, check that your runtime loads ESM (the package is pure ESM), read src/examples/ for the CLI, server and browser setups, and confirm how your deployment handles a file that is rewritten on every db.write().

## FAQ

### What is lowdb?

It is a small local JSON database for Node, Electron and the browser, described in package.json as a "Tiny local JSON database". Your data is a plain JavaScript object held in memory and written to a JSON file through an adapter.

### How do you use lowdb in a Node project?

Install it with npm install lowdb, then call JSONFilePreset with a filename and default data to get a database instance. Mutate db.data with normal JavaScript and call db.write(), or use db.update(fn) to change and save in one step.

### How does lowdb compare with MongoDB?

MongoDB runs as a separate server and is built for data shared across machines, while lowdb reads and writes a local JSON file with no server process. lowdb holds the whole dataset in memory and has no query language beyond native Array methods.

## Sources

- [Issues](https://github.com/typicode/lowdb/issues)
- [License: MIT](https://github.com/typicode/lowdb/blob/main/LICENSE)
- [README](https://github.com/typicode/lowdb/blob/main/README.md)
- [Releases](https://github.com/typicode/lowdb/releases)
- [typicode/lowdb on GitHub](https://github.com/typicode/lowdb)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/typicode-lowdb
