conf

Simple, schema-validated config file persistence for Node.js apps and CLI tools

Library
npm
v15.1.0
1,346 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
74 /100 Good
Development Activity 72
Maintenance 60
Community 64
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
82 /100 Excellent
Architecture 78
Code Quality 82
Innovation 80
Learning Curve 88

conf is a small, focused npm package by Sindre Sorhus for persisting settings to disk without having to think about file paths, serialization, or atomic writes. You hand it a project name (or a custom directory), and it stores a JSON file in the correct OS-specific user config location, exposing a simple get/set/has/delete API with dot-notation support for nested keys.

Beyond basic storage, conf layers in the features that config handling code tends to reinvent: JSON Schema validation via AJV so invalid writes throw instead of silently corrupting the store, a version-keyed migrations system for evolving the config shape across app releases, atomic writes so a crash mid-save can’t leave a half-written file, optional AES encryption/obfuscation of the file contents, and file-watching with change-event callbacks for cases where multiple processes touch the same store.

What You Get

  • A get/set/has/delete/clear API with dot-notation access to nested properties (e.g. config.set('foo.bar', true))
  • Automatic, OS-correct config file placement via env-paths, or a custom cwd when you need to override it
  • JSON Schema validation (via AJV, draft 2020-12) so writes that violate your schema throw immediately instead of corrupting the store
  • A version-keyed migrations system with a beforeEachMigration hook for evolving the config shape across app releases
  • Atomic writes (via atomically) so a crash mid-save never leaves a half-written config file
  • Optional AES-256 encryption (cbc/gcm/ctr) to obscure the file contents from casual editing
  • File watching with onDidChange/onDidAnyChange callbacks for reacting to changes made by another process
  • Full TypeScript types, including typed dot-notation key/value inference derived from your config’s generic type parameter

Common Use Cases

  • Persisting user preferences or window state for a CLI tool or desktop app between runs
  • Storing per-project or per-machine settings that need to survive across npm package versions with schema migrations
  • Caching small amounts of local data (API tokens, last-used values, feature flags) without pulling in a database dependency
  • Sharing a single settings file across multiple processes with watch: true to react to external changes

Under The Hood

Architecture — conf is a single Conf class (source/index.ts, ~900 lines) that wraps a JSON file on disk. The constructor pipeline in the file resolves options, sets up an AJV validator from an optional schema, applies default values, configures serialize/deserialize functions, resolves the file path via env-paths (or a caller-supplied cwd), and then reads/initializes the store — optionally starting an fs.FSWatcher if watch is set. Reads and writes go through private _get/_set methods layered on top of dot-prop’s getProperty/setProperty/deleteProperty for nested-key access, with an internal reserved __internal__.migrations.version key used to track schema migration state separately from user data.

Tech Stack — Pure TypeScript (100% of the codebase per GitHub’s language breakdown), published as an ESM-only package ("type": "module") requiring Node.js >=20. Key dependencies: ajv/ajv-formats (JSON Schema draft 2020-12 validation), atomically (crash-safe atomic file writes), dot-prop (dot-notation property access), env-paths (cross-platform config directory resolution), debounce-fn (debouncing the file-watch change handler), semver (parsing migration version ranges), and uint8array-extras for binary/encryption handling. The build step (tsc) compiles to dist/source with generated .d.ts files as the sole published output.

Code Quality — The project is maintained by Sindre Sorhus and uses his xo linter (a strict ESLint preset) plus tsc --noEmit as part of npm test, alongside a substantial test suite under test/ (index.ts at ~41KB, plus dedicated migrations.ts, advanced-features.ts, and a index.test-d.ts type-level test file, run via tsx --test). Error handling is explicit and deliberate: malformed JSON, schema violations, and decryption failures each throw descriptive errors (or clear the store, if clearInvalidConfig is set), and private methods are named with a leading underscore or true # private fields to clearly separate internal machinery from the public API.

API Design — The public surface is intentionally small — get, set, has, delete, clear, reset, appendToArray, plus onDidChange/onDidAnyChange — and getting started requires only new Conf({projectName: 'foo'}) with zero required configuration beyond a project name. TypeScript generics propagate through to dot-notation key/value types, so config.get('foo.bar') is typed correctly for a caller-supplied config shape without extra annotation. The tradeoff for this simplicity is documented plainly in the README: it explicitly does not support concurrent multi-process writes to the same store, and it is not intended as a database for anything beyond small settings data.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers