Getting Started
Use one key-value API across memory, filesystems, databases, cloud services, and browser storage.
Unstorage separates your application from its storage backend. Start with the built-in memory driver, then switch drivers or mount several drivers on the same storage instance without changing the rest of your code.
#Installation
npm i unstorage#Quick start
import { createStorage } from "unstorage";
const storage = createStorage();
await storage.setItem("user:1", { name: "Ada" });
const user = await storage.getItem("user:1");
// { name: "Ada" }The default driver stores data in memory. To persist data, pass a driver:
import { createStorage } from "unstorage";
import fsDriver from "unstorage/drivers/fs";
const storage = createStorage({
driver: fsDriver({ base: "./data" }),
});Note
Keys are normalized to colon-separated paths. For example, users/1, /users/1, and users:1 address the same key: users:1.
#Core API
Data operations return promises even when the underlying driver is synchronous. Mount registration and inspection methods are synchronous.
#Reading values
#hasItem(key, options?)
Checks whether a key exists.
if (await storage.hasItem("user:1")) {
// ...
}Alias: storage.has().
#getItem(key, options?)
Returns the deserialized value, or null when the key does not exist.
const value = await storage.getItem("user:1");Alias: storage.get().
#getItems(items, commonOptions?)
Warning
The batch APIs are experimental and may change in a future release.
Reads several keys. Each entry can be a key or an object with per-item options. Unstorage groups operations by mounted driver and uses the driver's batch implementation when available.
const items = await storage.getItems([
"user:1",
{ key: "user:2", options: { consistency: "strong" } },
]);
// [{ key: "user:1", value: ... }, { key: "user:2", value: ... }]#Writing values
#setItem(key, value, options?)
Serializes and stores a JSON-compatible value. Passing undefined removes the key.
await storage.setItem("settings:theme", "dark");
await storage.setItem("settings:layout", { compact: true });Alias: storage.set().
#setItems(items, commonOptions?)
Stores several values. Per-item options are merged with the common options.
await storage.setItems([
{ key: "feature:a", value: true },
{ key: "feature:b", value: false, options: { ttl: 60 } },
]);This experimental method resolves when all writes complete.
#removeItem(key, options?)
Removes a key. Set removeMeta to also remove custom metadata stored for that key.
await storage.removeItem("user:1", { removeMeta: true });Aliases: storage.remove() and storage.del().
#Raw values
Warning
getItemRaw and setItemRaw are experimental. Driver support and returned types vary. Follow unjs/unstorage#142 for details.
Use the raw API for binary data or a driver's native value format:
await storage.setItemRaw("images:logo.png", new Uint8Array([1, 2, 3]));
const bytes = await storage.getItemRaw<Uint8Array>("images:logo.png");When a driver does not implement raw operations, unstorage uses a serialized fallback.
#Listing and clearing keys
#getKeys(base?, options?)
Returns full, normalized keys. A base limits the result to that prefix. Custom metadata keys are excluded.
await storage.getKeys();
// ["settings:theme", "users:1", "users:2"]
await storage.getKeys("users");
// ["users:1", "users:2"]
await storage.getKeys("users", { maxDepth: 1 });Alias: storage.keys().
#clear(base?, options?)
Without a base, clears every writable mount. With a base, clears mounted drivers whose mount points match that base or a descendant.
await storage.clear(); // clear all mounts
await storage.clear("cache"); // clear mounts at or below "cache"Caution
clear(base) targets matching mounts; it does not remove an arbitrary key prefix from the default or a parent driver. To remove such a prefix, list getKeys(base) and call removeItem for each key.
#Metadata
Unstorage merges two metadata sources:
- native driver metadata, such as filesystem modification time and size;
- custom metadata stored by
setMeta.
Custom metadata overrides fields with the same name from the driver.
await storage.setMeta("report", { author: "Ada", ttl: 3600 });
const meta = await storage.getMeta("report");
await storage.removeMeta("report");Pass { nativeOnly: true } to getMeta to skip custom metadata:
const nativeMeta = await storage.getMeta("report", { nativeOnly: true });Metadata support varies by driver. Transaction options such as ttl are also driver-specific; check the relevant driver page.
#Mounting multiple drivers
Mounts route a key prefix to another driver. The most specific matching mount wins.
import { createStorage } from "unstorage";
import fsDriver from "unstorage/drivers/fs";
import redisDriver from "unstorage/drivers/redis";
const storage = createStorage();
storage.mount("files", fsDriver({ base: "./data" }));
storage.mount("cache", redisDriver({ base: "my-app" }));
await storage.setItem("files:report", "persistent");
await storage.setItem("cache:user:1", { name: "Ada" });
await storage.setItem("temporary", true); // default memory drivermount(base, driver) is synchronous and returns the storage instance. A mount receives keys relative to its base.
storage.getMount("cache:user:1");
// { base: "cache:", driver: ... }
storage.getMounts("cache");
// [{ base: "cache:", driver: ... }]
await storage.unmount("cache"); // disposes the driver by defaultPass false as the second argument to unmount to keep the driver open.
#Watching changes
Register a listener for "update" and "remove" events:
const stop = await storage.watch((event, key) => {
console.log(event, key);
});
await storage.setItem("status", "ready");
await stop();Drivers with native watch support can report external changes. For drivers without watch, unstorage emits events for individual setItem, setItemRaw, and removeItem calls. Batch writes and clear do not currently synthesize events. Call storage.unwatch() to remove every listener.
#Cleanup
Call dispose() when your application shuts down so drivers can close clients, watchers, timers, and other resources:
await storage.dispose();For the memory driver, disposing also clears its data.
#TypeScript
#Type individual reads
getItem always includes null because a key may not exist.
type User = { name: string };
const user = await storage.getItem<User>("user:1");
// User | null
const bytes = await storage.getItemRaw<Uint8Array>("logo");
// Uint8Array | null#Type the whole storage
import { createStorage } from "unstorage";
const textStorage = createStorage<string>();
await textStorage.setItem("greeting", "hello");
// await textStorage.setItem("count", 1); // TypeScript errorYou can define known keys with different value types:
type AppStorage = {
items: {
theme: "light" | "dark";
visitCount: number;
};
};
const appStorage = createStorage<AppStorage>();
await appStorage.setItem("theme", "dark");
const visits = await appStorage.getItem("visitCount");
// number | nullUnknown string keys still use the general StorageValue type.
For a typed namespace, use prefixStorage.