unstorage

A universal async key-value storage API with pluggable drivers for memory, filesystem, Redis, databases, and 30+ cloud platforms.

Library
npm
v1.17.5
2,640stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
88/100Excellent
Development Activity96
Maintenance96
Community60
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
86/100Excellent
Architecture88
Code Quality90
Innovation82
Learning Curve85

Unstorage is a tiny, dependency-free abstraction over key-value storage that gives every backend the same async API: getItem, setItem, removeItem, getKeys, and clear. Instead of writing separate code paths for an in-memory cache in development, the filesystem in a Node server, and Redis or a cloud KV store in production, you write your storage logic once against unstorage’s Storage interface and swap the underlying driver by configuration alone.

Unix-style mounting lets you compose several drivers into one logical storage tree (for example, mounting a fast in-memory cache over a slower persistent driver), and built-in support for metadata, change watching, snapshots, and an optional HTTP server make it a common building block inside frameworks like Nitro and Nuxt as well as standalone apps that just need a portable cache or config store.

What You Get

  • A consistent async Storage API (getItem/setItem/removeItem/getKeys/clear) that behaves identically regardless of which driver is mounted
  • 30+ official drivers covering memory, filesystem, Redis, MongoDB, Cloudflare KV/R2/Cache, Azure, AWS S3, Vercel Blob, Planetscale, Upstash, GitHub, HTTP, IndexedDB, localStorage, Deno KV, and more
  • Unix-style driver mounting so multiple backends can be combined into one storage tree, plus prefixStorage() for cheap namespacing
  • Metadata handling (getMeta/setMeta with atime/mtime/ttl), a watch() API for change notifications, and snapshot()/restoreSnapshot() helpers for dumping and reloading state
  • An optional HTTP server (unstorage/server) that exposes any storage instance over a REST API, and an OpenTelemetry tracing wrapper for observability

Common Use Cases

  • Giving a framework or library a pluggable cache/config layer that end users can point at memory, Redis, or a cloud KV store without changing the framework’s own code
  • Building an app-level cache that falls back between a fast in-memory driver and a slower persistent one via mounting
  • Sharing key-value state across serverless/edge functions using a cloud driver (Cloudflare KV, Vercel Blob, Upstash) behind the same local API used in development
  • Exposing a storage instance as an HTTP API for other services or tools to read/write without a direct dependency

Under The Hood

Architecture The core (src/storage.ts) exposes createStorage() which manages a StorageCTX holding a mounts map keyed by normalized base paths and a sorted mountpoints array for longest-prefix matching, defaulting to an in-memory driver at the root. Every operation (getItem, setItem, getKeys, clear, watch) resolves its target driver through getMount(), then dispatches via asyncCall (from _utils.ts) so synchronous and Promise-returning drivers are handled uniformly. Values are always stored as strings, passed through destr on read and stringify() on write, which keeps the Driver interface itself minimal (hasItem/getItem/setItem/getKeys required, with getItemRaw/setItemRaw/getItems/setItems/watch/dispose/getMeta as optional extensions). Change notifications flow through a single onChange callback fanned out to registered watchListeners, and mounting/unmounting a driver at runtime just adds or removes an entry in the mounts map with no restart required. This mount-based routing is the one abstraction the whole library is organized around.

Tech Stack unstorage’s own runtime footprint is essentially dependency-free: destr is the only production import used directly in storage.ts, while everything else (Azure SDKs, @vercel/blob, ioredis, mongodb, @planetscale/database, @upstash/redis, wrangler, and more) is a devDependency backing exactly one optional driver under src/drivers/, dynamically imported only when that driver is mounted. Build tooling is obuild, paired with a custom scripts/gen-drivers.ts codegen step that produces the auto-generated src/_drivers.ts driver registry and type union; linting/formatting run through oxlint and oxfmt, and tests run on Vitest with coverage via @vitest/coverage-v8. Distribution is ESM-only (type: module), with a dedicated server subpath (unstorage/server, built on srvx) and per-driver subpaths (unstorage/drivers/*).

Code Quality Tests live under test/ (storage.test.ts, server.test.ts, driver-dependencies.test.ts, storage.test-d.ts for type-level assertions, and a test/drivers/ suite exercising each built-in driver against a real or mocked backend such as ioredis-mock, fake-indexeddb, mongodb-memory-server, and azurite). CI runs a build, tsc —noEmit —skipLibCheck, vitest with coverage, and oxlint on every push/PR, then uploads coverage to Codecov and auto-publishes on tag. The codebase is fully typed with explicit Driver/Storage/DriverDependency interfaces, and behavior on unsupported operations (e.g. writing to a read-only driver) is a documented no-op rather than a silently swallowed error.

API Design Getting started requires only createStorage() with no configuration, defaulting to an in-memory driver and routing through asyncCall so sync and async drivers behave identically. The surface is intentionally small and uniform: every driver exposes the same handful of methods, so switching from memory to Redis, S3, or Cloudflare KV is a one-line mount() change with no other code changes. Convenience aliases (get/set/has/del) mirror the verbose method names, prefixStorage() gives namespacing without re-wiring mounts, and snapshot()/restoreSnapshot() cover dumping and reloading state out of the box. A dedicated documentation site with a guide plus a reference page per driver meaningfully lowers the cost of adopting a new backend.

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

Search