Unstorage
A collection of skills for use with AI coding tools
npx -y skills add BetaHuhn/skills --skill unstorageAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Guide for setting up and using the unstorage npm package — a universal key-value storage API with 20+ built-in drivers. Use this skill when the user is working with unstorage, importing from "unstorage" or "unstorage/drivers/*", asking about storage drivers (memory, filesystem, Redis, Cloudflare KV, MongoDB, etc.), key-value persistence, multi-storage mounting, prefixStorage, snapshots, or building a custom driver. Trigger on mentions of "unstorage", "createStorage", "storage driver", or "universal key-value".
SKILL.md
12.9 KB, as published. Nobody here has run it
unstorage
unstorage is a runtime-agnostic universal key-value storage API with a unified interface and 20+ built-in drivers. It works on Node.js, Bun, Deno, and edge Workers.
Install:
npm install unstorage
Core concepts
- All keys use
foo:barcolon-separated convention (equivalent tofoo/barpath-style) - The default driver is in-memory (
Map-based) — no config needed to get started - All methods are async and return Promises
- Values are automatically JSON-serialized/deserialized
- Drivers are mounted Unix-style at arbitrary namespace prefixes
Basic usage
import { createStorage } from "unstorage";
const storage = createStorage(/* opts */);
await storage.setItem("foo:bar", { hello: "world" });
await storage.getItem("foo:bar"); // => { hello: "world" }
await storage.hasItem("foo:bar"); // => true
await storage.removeItem("foo:bar");
Fetching up-to-date documentation
unstorage provides LLM-optimized docs. Fetch these when you need precise details on a specific driver or topic:
- Full docs index:
https://unstorage.unjs.io/llms.txt - Complete docs (single file):
https://unstorage.unjs.io/llms-full.txt
Individual pages (Markdown):
https://unstorage.unjs.io/raw/guide.md— Getting startedhttps://unstorage.unjs.io/raw/guide/utils.md— Utilitieshttps://unstorage.unjs.io/raw/guide/http-server.md— HTTP serverhttps://unstorage.unjs.io/raw/guide/custom-driver.md— Custom driverhttps://unstorage.unjs.io/raw/drivers.md— All drivers overviewhttps://unstorage.unjs.io/raw/drivers/{driver-name}.md— Specific driver (e.g.redis,fs,cloudflare,mongodb)
Always fetch the relevant driver's raw Markdown before writing driver-specific code — driver options, peer dependencies, and examples can vary significantly.
Full API reference
Read
await storage.hasItem("key"); // boolean — also: storage.has()
await storage.getItem("key"); // value | null — also: storage.get()
await storage.getItems([ // batch read (experimental)
"key1",
{ key: "key2", options: {} },
]); // => [{ key, value }, ...]
await storage.getItemRaw("file.bin"); // raw Buffer/Uint8Array
await storage.getKeys("base?"); // string[] of all keys — also: storage.keys()
await storage.getMeta("key"); // { mtime, atime, size, ...custom }
Write
await storage.setItem("key", value); // also: storage.set()
await storage.setItems([{ key, value }]); // batch write (experimental)
await storage.setItemRaw("file.bin", buffer); // raw binary write
await storage.setMeta("key", { flag: 1 }); // custom metadata (stored at key$)
Delete
await storage.removeItem("key"); // also: storage.del(), storage.remove()
await storage.removeItem("key", { removeMeta: true }); // also remove metadata
await storage.removeMeta("key");
await storage.clear("base?"); // remove all (optionally under a base)
Lifecycle
await storage.dispose(); // close open handles (call before process exit)
Watch
const unwatch = await storage.watch((event, key) => {
// event: "update" | "remove"
});
await unwatch(); // stop this specific watcher
await storage.unwatch(); // stop all watchers
TypeScript generics
// Type the return value of getItem
await storage.getItem<string>("k"); // => string | null
// Type an entire storage instance
const storage = createStorage<string>();
storage.setItem("k", "val"); // OK
storage.setItem("k", 123); // TS error
// Type a storage namespace (key map)
type StorageDefinition = {
items: { foo: string; count: number };
};
const storage = createStorage<StorageDefinition>();
Drivers
Pass a driver to createStorage({ driver }) to change the backend. The default is memory.
Memory (default)
import { createStorage } from "unstorage";
import memoryDriver from "unstorage/drivers/memory";
const storage = createStorage({ driver: memoryDriver() });
Filesystem (Node.js)
Requires no extra install. Maps keys to files on disk. Supports watch via chokidar.
import { createStorage } from "unstorage";
import fsDriver from "unstorage/drivers/fs";
const storage = createStorage({
driver: fsDriver({ base: "./data" }),
});
// Options: base, ignore, watchOptions
Lite variant (no extra dependencies):
import fsLiteDriver from "unstorage/drivers/fs-lite";
const storage = createStorage({ driver: fsLiteDriver({ base: "./data" }) });
Redis
npm install ioredis
import { createStorage } from "unstorage";
import redisDriver from "unstorage/drivers/redis";
const storage = createStorage({
driver: redisDriver({
base: "app", // key namespace prefix
host: "localhost",
port: 6379,
// password, tls, url, ttl, scanCount, preConnect, cluster, clusterOptions
}),
});
// Per-item TTL
await storage.setItem("session:abc", data, { ttl: 3600 }); // seconds
Cloudflare KV
import cloudflareKVBindingDriver from "unstorage/drivers/cloudflare-kv-binding";
// Inside a Worker:
const storage = createStorage({
driver: cloudflareKVBindingDriver({ binding: "MY_KV" }),
});
MongoDB
npm install mongodb
import mongodbDriver from "unstorage/drivers/mongodb";
const storage = createStorage({
driver: mongodbDriver({
connectionString: "mongodb://localhost:27017",
databaseName: "mydb",
collectionName: "storage",
}),
});
Browser (localStorage / sessionStorage / IndexedDB)
import localStorageDriver from "unstorage/drivers/localstorage";
import sessionStorageDriver from "unstorage/drivers/session-storage";
import indexedDbDriver from "unstorage/drivers/indexedb";
const storage = createStorage({ driver: localStorageDriver({ base: "app:" }) });
Other built-in drivers
| Driver | Import | Notes |
|---|---|---|
| LRU Cache | unstorage/drivers/lru-cache | In-memory, requires lru-cache |
| GitHub | unstorage/drivers/github | Read-only, maps repo files |
| HTTP | unstorage/drivers/http | Remote HTTP endpoint |
| Vercel KV | unstorage/drivers/vercel-kv | Requires @vercel/kv |
| Netlify Blobs | unstorage/drivers/netlify-blobs | |
| Azure | unstorage/drivers/azure-* | Multiple Azure storage services |
| SQL Database | unstorage/drivers/db | Requires db0 |
| PlanetScale | unstorage/drivers/planetscale |
Full list: https://unstorage.unjs.io/drivers
Multi-storage mounting
Mount additional drivers at specific key namespaces. Keys routed by longest matching prefix.
import { createStorage } from "unstorage";
import fsDriver from "unstorage/drivers/fs";
import redisDriver from "unstorage/drivers/redis";
const storage = createStorage({}); // default: memory
// Filesystem for assets
storage.mount("assets", fsDriver({ base: "./public" }));
// Redis for sessions
storage.mount("sessions", redisDriver({ host: "localhost" }));
// => storage.setItem("assets:logo.png", ...) → writes to ./public/logo.png
// => storage.setItem("sessions:abc", ...) → writes to Redis
// => storage.setItem("config:foo", ...) → writes to memory
Mount options:
storage.mount("logs", fsDriver({ base: "./logs" }), {
readOnly: true, // disable setItem / setItemRaw
noClear: true, // disable clear()
});
Inspect mounts:
storage.getMount("sessions:abc");
// => { base: "sessions:", driver: ... }
storage.getMounts("sessions");
// => [{ base: "sessions:", driver }]
await storage.unmount("sessions"); // unregister + dispose
Namespaced sub-storage
Create a scoped instance that prefixes all operations — useful for module isolation:
import { createStorage, prefixStorage } from "unstorage";
const storage = createStorage();
const userStorage = prefixStorage(storage, "users");
await userStorage.setItem("alice", { role: "admin" });
// Same as: storage.setItem("users:alice", { role: "admin" })
await userStorage.getKeys();
// Returns keys WITHOUT the "users:" prefix
Snapshots
Serialize all items to/from a plain object — useful for testing, seeding, or backups:
import { snapshot, restoreSnapshot } from "unstorage";
// Capture
const data = await snapshot(storage, "config"); // base is optional
// => { "theme": "dark", "lang": "en" }
// Restore
await restoreSnapshot(storage, data, "config");
Custom driver
Implement a driver using defineDriver. Only implement the methods you need.
import { createStorage, defineDriver } from "unstorage";
const myDriver = defineDriver((options: { prefix?: string } = {}) => {
const map = new Map<string, string>();
return {
name: "my-driver",
options,
async hasItem(key) { return map.has(key); },
async getItem(key) { return map.get(key) ?? null; },
async setItem(key, value) { map.set(key, value); },
async removeItem(key) { map.delete(key); },
async getKeys(base) {
return [...map.keys()].filter(k => !base || k.startsWith(base));
},
async clear(base) {
for (const key of await this.getKeys(base)) map.delete(key);
},
async dispose() { map.clear(); },
// async watch(callback) { ... } // optional
};
});
const storage = createStorage({ driver: myDriver({ prefix: "app" }) });
Custom driver rules:
- Keys follow
foo:barcolon convention getItemreturns a string, serializable object, ornull- If you implement
watch, unstorage's default change events are disabled — you must emit them manually ongetItem,setItem, andremoveItem - Clean up open handles (timers, connections) in
dispose()
HTTP server
Expose a storage instance over HTTP for remote access:
npm install listhen
// server.ts
import { listen } from "listhen";
import { createStorage } from "unstorage";
import { createStorageServer } from "unstorage/server";
const storage = createStorage();
const storageServer = createStorageServer(storage, {
authorize(req) {
// req: { key, type, event }
if (req.type === "read" && req.key.startsWith("private:")) {
throw new Error("Unauthorized");
}
},
});
await listen(storageServer.handle);
HTTP method mapping:
GET /key→getItem(key)GET /base/→getKeys(base)HEAD /key→hasItem(key)(returns 404 if missing)PUT /key→setItem(key, body)DELETE /key→removeItem(key)DELETE /base/→clear(base)
Connect a client using the HTTP driver:
import httpDriver from "unstorage/drivers/http";
const client = createStorage({
driver: httpDriver({ base: "http://localhost:3000" }),
});
Common patterns
Caching with TTL (Redis)
const cache = createStorage({
driver: redisDriver({ host: "localhost", ttl: 60 }),
});
await cache.setItem("result:1", data, { ttl: 300 }); // 5 min TTL
Feature flag / config store
const config = prefixStorage(storage, "config");
await config.setItem("feature:dark-mode", true);
const enabled = await config.getItem<boolean>("feature:dark-mode");
Layered storage (overlay pattern)
Use overlayDriver to stack a writeable layer on top of a read-only base:
import overlayDriver from "unstorage/drivers/overlay";
import fsDriver from "unstorage/drivers/fs";
import memoryDriver from "unstorage/drivers/memory";
const storage = createStorage({
driver: overlayDriver({
layers: [memoryDriver(), fsDriver({ base: "./defaults" })],
}),
});
Common pitfalls
- Missing
await— all storage methods are async; forgettingawaitsilently does nothing - Key format inconsistency — prefer
foo:baroverfoo/bar; both work but mixing causes confusion - Not calling
dispose()— drivers with open handles (Redis, DB connections, FS watchers) will leak if not disposed before process exit - Mutating objects from
getItem— the returned value may be a reference; clone it if needed - No TTL support in all drivers — TTL is only supported where the underlying store supports it (e.g. Redis); memory driver ignores
ttl watchin custom drivers — if you implementwatch, you must emit events yourself; unstorage's default handler is disabled
Further reading
- Drivers reference: https://unstorage.unjs.io/drivers
- Custom driver guide: https://unstorage.unjs.io/guide/custom-driver
- HTTP server guide: https://unstorage.unjs.io/guide/http-server
- Utilities: https://unstorage.unjs.io/guide/utils