Typed Storage
Fully typed, async, key-prefixed storage over any backend.
@nice-code/util provides typed storage adapters and WebCrypto utilities.
bun add @nice-code/utilITypedStorage
Section titled “ITypedStorage”ITypedStorage<T> gives you fully typed, async, key-prefixed storage over any backend.
import { createTypedWebLocalStorage } from "@nice-code/util";
interface IAppStorage { user_id: string; theme: "light" | "dark"; recent_searches: string[];}
const storage = createTypedWebLocalStorage<IAppStorage>({ localStorage, keyPrefix: "app:",});
// All keys autocomplete; values are typedawait storage.setJson("theme", "dark");const theme = await storage.getJson("theme"); // "light" | "dark" | undefinedconst userId = await storage.getJsonOrDef("user_id", "guest"); // string
// Read-modify-write in one callawait storage.updateJsonWithDef("recent_searches", [], (cur) => [...cur, "query"]);
await storage.removeItem("theme");await storage.clearAll(); // removes only keys this storage has writtenAdapters
Section titled “Adapters”import { createTypedWebLocalStorage, createTypedWebSessionStorage, createDurableObjectTypedStorage, createTypedMemoryStorage_string, createTypedMemoryStorage_json,} from "@nice-code/util";
// Browserconst local = createTypedWebLocalStorage<IAppStorage>({ localStorage, keyPrefix: "app:" });const session = createTypedWebSessionStorage<IAppStorage>({ sessionStorage });
// Cloudflare Durable Objects (inside a DO class)const doStorage = createDurableObjectTypedStorage<IDOStorage>({ durableObjectStorage: ctx.storage, keyPrefix: "do:",});
// In-memory (testing / SSR) — string-serialized or JSON-nativeconst mem = createTypedMemoryStorage_string<IAppStorage>();const memJson = createTypedMemoryStorage_json<IAppStorage>();
// Share state between instances by passing the same Mapconst shared = new Map<string, string>();const a = createTypedMemoryStorage_string<IAppStorage>({ memoryStorageMap: shared });const b = createTypedMemoryStorage_string<IAppStorage>({ memoryStorageMap: shared });The interface
Section titled “The interface”interface ITypedStorage<T extends Record<string, any>> { getJson<K>(key: K): Promise<T[K] | undefined>; getJsonOrDef<K>(key: K, defVal: T[K]): Promise<T[K]>; setJson<K>(key: K, val: T[K]): Promise<void>; updateJson<K>(key: K, updater: (cur: T[K] | undefined) => T[K]): Promise<void>; updateJsonWithDef<K>(key: K, defVal: T[K], updater: (cur: T[K]) => T[K]): Promise<void>; removeItem<K>(key: K): Promise<void>; clearAll(): Promise<void>;}Custom backend
Section titled “Custom backend”Implement the methods interface to wrap any storage (Redis, KV, …):
import { createTypedStorage, EStorageAdapterType, StorageAdapter, type IStorageAdapterMethods_String,} from "@nice-code/util";
const redisMethods: IStorageAdapterMethods_String = { type: EStorageAdapterType.string, getItem: async (key) => redis.get(key), setItem: async (key, value) => { await redis.set(key, value); }, removeItem: async (key) => { await redis.del(key); },};
const storage = createTypedStorage<IMySchema>({ storageAdapter: new StorageAdapter({ methods: redisMethods, keyPrefix: "app:" }),});The lower-level StorageAdapter can also be used directly (untyped keys), including createJsonGetterSetter<T>(key) for a single-key { get, set } pair.
Next: Crypto →