# sindresorhus/conf for Small Persistent Node.js Settings

> conf is a TypeScript package for persisting small JSON settings from Node.js applications and modules. It supports schema validation, nested dot-notation access, defaults, migrations, and atomic writes, but it is not a database and does not support concurrent writers to one store.

**sindresorhus/conf** — Simple config handling for your app or module

- Repository: https://github.com/sindresorhus/conf
- Stars: 1,351 · Forks: 139
- Language: TypeScript
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/sindresorhus-conf

## Why conf Exists for Command-Line Settings

conf solves a narrow but common Node.js problem: an application or module needs to remember a small amount of configuration between executions without implementing file discovery, JSON persistence, defaults, and key updates itself. The README says the project was initially made for command-line tools to persist data. That origin is reflected in the package description, which calls it “Simple config handling for your app or module.”

The intended data is small user-facing state such as preferences, selected options, flags, or other settings that change occasionally. conf stores the data as JSON, so callers can use strings, numbers, booleans, arrays, and objects as long as the values are JSON serializable. A value assigned to undefined is ignored. Values with a `toJSON` method, including examples such as `Date`, are stored as their serialized representation.

This is not a general-purpose persistence service. The README explicitly says that conf is not a database because the entire JSON file is read and written on every change. It also says the package does not support two processes writing to the same store at the same moment. Those boundaries make it more suitable for a CLI that stores a handful of preferences than for a service that needs transactions, large collections, or coordinated writers.

## How conf Stores and Updates Configuration

A `Conf` instance represents one configuration store. Each change reads the current store, applies the requested mutation, and writes the complete JSON file. The API supports whole-store assignment through `config.store`, while methods such as `.set()`, `.get()`, `.has()`, `.delete()`, `.reset()`, and iteration operate on the stored data. The README states that writes are atomic, which means a process crash during a write should not corrupt the existing config file.

Nested properties use dot notation. For example, `config.set('foo.bar', true)` creates an object under `foo`, so reading `foo` returns `{bar: true}`. Dots in literal property names can be escaped. The documentation gives `sites.example\.com` as the escaped notation and `'sites.example\\.com'` as the JavaScript string literal.

The package also supports JSON Schema validation through AJV. The `schema` option supplies the `properties` object, while `rootSchema` supplies root-level keywords such as `additionalProperties` and `patternProperties`. AJV uses JSON Schema draft 2020-12. By default, `allErrors` and `useDefaults` are enabled, although `ajvOptions` can override those settings.

The store is deliberately file-backed rather than an in-memory database with a background write queue. A whole-store write replaces the contents of the file. Two writes can overlap, and the README warns that concurrent writes are unsupported. The atomic-write behavior protects the file from partial corruption, but it does not turn the package into a multi-process locking mechanism.

## Installing conf and Creating a First Store

The README's usage example is:

```js
import Conf from 'conf';

const config = new Conf({projectName: 'foo'});

config.set('unicorn', '🦄');
console.log(config.get('unicorn'));
//=> '🦄'

config.set('foo.bar', true);
console.log(config.get('foo'));
//=> {bar: true}

config.delete('unicorn');
console.log(config.get('unicorn'));
//=> undefined
```

The example uses JavaScript syntax even though the project is written in TypeScript. The package exports `Conf` as its default entry point and also exposes the `Schema` type, which can be used to give `get()` more precise TypeScript return types.

## Schema Validation, Defaults, and TypeScript Results

Schema support is useful when configuration has a known contract. The README shows a schema with a numeric `foo` property constrained to a range and a string `bar` property using the URL format. If the schema supplies a default, reading `foo` returns `50`. Assigning the string `'1'` produces a schema violation because `foo` must be a number.

Defaults can be supplied through the `defaults` option instead of schema properties. The precedence is explicit: values in `defaults` overwrite the `default` key in `schema`. Defaults are copied when the store is created, so later changes to the original object do not alter the store. A key covered by `defaults` cannot be deleted because the default is written back. A child beneath a defaulted key can be removed with dot notation, but `.reset()` restores it.

For TypeScript, the documentation recommends annotating the schema with `Schema<T>`. This lets `config.get('isEnabled')` be typed as `boolean` and `config.get('interval')` as `number`. Without the annotation, the README says `get()` returns `unknown`. There is one inference constraint to account for: when a key exists only in `defaults`, annotating the schema with `Schema<T>` still makes that key an error because the schema does not describe it.

Root-level validation is available when property names need to be restricted. `rootSchema` can set `additionalProperties: false`, and `patternProperties` can validate keys that are not known in advance. The `properties` keyword belongs in `schema`, not `rootSchema`; placing it in `rootSchema` throws according to the README.

## Migrations, Atomic Writes, and Upgrade Boundaries

conf includes a migrations mechanism for applying version-specific operations when a project's version is upgraded. The `migrations` object maps a version or semver range to a handler. Handlers run in version order, regardless of the order in which they are declared.

The migration design has several constraints that matter during upgrades. A migration must not create another `Conf` instance with migrations for the same config file. It sees the keys held by the config file and the values from the `defaults` option. Schema defaults are added after migrations run, allowing a migration to repair a value that would otherwise fail schema validation. Migration bookkeeping is stored under the reserved `__internal__` key, which is hidden from `.store`, `.get()`, `.has()`, and iteration, and `.set()` and `.delete()` reject it.

The recorded version never moves backwards. The README does not provide a complete troubleshooting or rollback procedure for migration failures. It also labels the feature as having known bugs, says the author cannot provide support for it, and has no plans to work on it. That warning matters for applications that depend on migrations to transform persisted data during a major release.

The package metadata lists v16.0.0 dated 2026-10-12, v15.1.0 dated 2026-02-04, and v15.0.2 dated 2025-10-05. The repository is not archived, and its last push was on 2026-10-12. The release history and recent push do not remove the documented migration warning, so upgrades involving stored configuration deserve direct review.

## When conf Is the Wrong Persistence Choice

The clearest wrong-tool case is a workload with large data or frequent changes. The README says the entire JSON file is read and written on every change. That design makes each operation proportional to the size of the store and creates a clear cost for applications that write on every keystroke, event, request, or automated task.

The second boundary is coordination. conf does not support two processes writing to the same store at the same moment. A write reads the file first, so an intervening change can be lost unless the two writes overlap. A whole-store assignment is more direct and more destructive: `config.store = ...` replaces everything the file holds. Applications that need independent processes to update one configuration file need a storage design with explicit concurrency control rather than relying on this package.

For large data or high-rate changes, the README points to `node:sqlite` or a similar database. For Electron applications, it points to `electron-store` instead. Those alternatives differ in approach: `node:sqlite` provides a database storage model, while `electron-store` is intended for Electron configuration. conf instead provides a small JSON file with schema validation and atomic replacement, which is a narrower and simpler contract.

## Conclusion

Adopt conf for command-line tools and small application settings that can be represented as JSON and change occasionally. Do not choose it for large datasets, high-frequency writes, or concurrent multi-process updates. Check the storage location and migration behavior against your application first, especially when upgrading across v15 and v16.

## FAQ

### What problem does sindresorhus/conf solve?

It provides simple persistent JSON configuration for a Node.js application or module, including defaults, nested access, schema validation, and file writes. The README says it was initially intended for command-line tools that need to persist data.

### How do I install and use sindresorhus/conf?

Install it with `npm install conf`, then create a `Conf` instance and use `.set()`, `.get()`, and `.delete()` to manage JSON values. The documented first example uses `new Conf({projectName: 'foo'})` and requires Node.js `>=22` for version 16.0.0.

### Can sindresorhus/conf handle concurrent writes and large data?

No. The README explicitly says that two processes should not write the same store at the same moment and that the whole JSON file is read and written on every change. It recommends `node:sqlite` or a similar database for large data or frequent changes.

## Sources

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

---

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