Skip to content

Latest commit

Β 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

@rabbx/kv

One API. Every Runtime. Zero Headaches.

@rabbx/kv is a cross-platform, type-safe Key-Value store abstraction. It provides a unified, modern async API that works identically across Node.js, Bun, Cloudflare Workers, Vercel KV, and the Browser. Stop rewriting your storage logic every time you switch runtimes or deploy targets.

✨ Features

  • Universal API: Standard get, set, del, has, clear + batch operations (getMany, setMany, delMany).
  • Multi-Runtime: Node.js, Bun, Cloudflare Workers, Vercel (Upstash), and Browser.
  • Pluggable Node Backends: In-memory, File-backed (JSON), LevelDB, and SQLite.
  • Binary & Serialization: Internal Uint8Array contract. Choose between json or msgpack (natively handles Dates, Maps, Sets, and binary data).
  • Per-Key TTL: Granular expiration control, fully supported inside batch operations.
  • Zero Core Dependencies: The core is dependency-free. You only install the specific database drivers you actually use.

πŸ“¦ Installation

Install the core package:

npm install @rabbx/kv

Optional Dependencies (Node.js Backends)

Only install the drivers for the backends you intend to use:

# For LevelDB backend
npm install level

# For SQLite backend
npm install better-sqlite3

# For Browser backend (if not already in your project)
npm install localforage

πŸš€ Quick Start

import { createKV } from '@rabbx/kv';

// 1. Initialize (Defaults to in-memory Node backend)
const kv = createKV({ platform: 'node' });

// 2. Use it
await kv.set('user:1', { name: 'Alice', role: 'admin' }, { ttl: 3600 });
const user = await kv.get<{ name: string; role: string }>('user:1');

console.log(user); // { name: 'Alice', role: 'admin' }

🌍 Platform Usage

Node.js (File-Backed)

Persists data to a local JSON file. Great for local dev, CLI tools, or small caches.

const kv = createKV({
  platform: 'node',
  backend: 'file',
  filePath: './data/cache.json',
  serializer: 'msgpack' // Optional: defaults to 'msgpack'
});

Node.js (SQLite)

High-performance, persistent local storage. Uses better-sqlite3 under the hood.

const kv = createKV({
  platform: 'node',
  backend: 'sqlite',
  dbPath: './data/local.db'
});

Node.js (LevelDB)

Fast, ordered, persistent key-value store.

const kv = createKV({
  platform: 'node',
  backend: 'level',
  dbPath: './data/level-db'
});

Bun (Native SQLite)

Bun ships with bun:sqlite built-in. The Bun adapter uses it natively, meaning zero extra dependencies and zero configuration required. It defaults to a rabbx-kv.sqlite file in your current directory.

const kv = createKV({ 
  platform: 'bun' 
  // dbPath: './custom-path.sqlite' // Optional: override default path
});

await kv.set('runtime', 'bun');

Browser (LocalForage)

Automatically uses IndexedDB, WebSQL, or LocalStorage depending on browser support.

import localforage from 'localforage';

const kv = createKV({
  platform: 'browser',
  localforageInstance: localforage.createInstance({
    name: 'my-app',
    storeName: 'kv_store'
  })
});

Cloudflare Workers

Wraps a Cloudflare KV Namespace.

export default {
  async fetch(request, env) {
    const kv = createKV({
      platform: 'cf',
      cfNamespace: env.MY_KV_NAMESPACE
    });

    await kv.set('edge:config', { featureFlags: true });
    return new Response('OK');
  }
};

Vercel / Upstash Redis

Uses @upstash/redis directly β€” the same engine that powers @vercel/kv, but with better typing and broader runtime support (Edge, CF Workers, Deno, etc.).

npm install @upstash/redis
import { Redis } from '@upstash/redis';
import { createKV } from '@rabbx/kv';

const redis = new Redis({
  url: process.env.UPSTASH_URL!,
  token: process.env.UPSTASH_TOKEN!,
});

const kv = createKV({
  platform: 'vercel',
  upstashClient: redis
});

await kv.set('session:abc', { userId: '123' }, { ttl: 3600 });

Why @upstash/redis over @vercel/kv?

  • @vercel/kv is just a thin wrapper around @upstash/redis with some Vercel-specific env var auto-detection
  • Using @upstash/redis directly gives you access to the full Redis API (pipelines, transactions, Lua scripts) if you ever need to drop down
  • Works identically in Vercel Edge, Cloudflare Workers, Node, Bun, and Deno
  • Better TypeScript support and more predictable typing

πŸ›  API Reference

Core Methods

Method Signature Description
get <T>(key: string): Promise<T | null> Retrieves a value. Returns null if missing or expired.
set <T>(key: string, value: T, opts?: { ttl?: number }): Promise<void> Sets a value. ttl is in seconds.
del (key: string): Promise<void> Deletes a key.
has (key: string): Promise<boolean> Checks if a key exists and is not expired.
clear (): Promise<void> Wipes all keys. (Throws on Cloudflare Workers).

Batch Methods

Method Signature Description
getMany <T>(keys: string[]): Promise<Record<string, T | null>> Fetches multiple keys. Returns a Record mapping keys to values (or null).
setMany <T>(entries: BatchEntry<T>[]): Promise<void> Sets multiple keys. Supports per-key TTL.
delMany (keys: string[]): Promise<void> Deletes multiple keys.

Batch Entry Shape:

interface BatchEntry<T> {
  key: string;
  value: T;
  ttl?: number; // Optional per-key TTL in seconds
}

βš™οΈ Configuration (KVConfig)

Property Type Description
platform 'node' | 'bun' | 'cf' | 'vercel' | 'browser' Required. The target runtime.
serializer 'json' | 'msgpack' Defaults to 'msgpack'. Use 'json' if you only store basic primitives and want to save bandwidth on remote KV.
backend 'memory' | 'file' | 'level' | 'sqlite' Node only. Defaults to 'memory'.
filePath string Node + File backend. Path to the JSON file.
dbPath string Node + Level/SQLite & Bun. Path to the database directory/file.
cfNamespace any CF. The Cloudflare KV namespace binding.
vercelClient any Vercel. The @vercel/kv client instance.
localforageInstance any Browser. Optional custom LocalForage instance.

🧠 Architecture & Trade-offs

  1. The Uint8Array Contract: Internally, all adapters speak Uint8Array. This decouples serialization from storage. LevelDB and SQLite store raw bytes natively (zero overhead), while File and Browser adapters handle Base64 translation seamlessly.
  2. SQLite Event Loop: better-sqlite3 (Node) and bun:sqlite (Bun) are strictly synchronous. For v1, we wrap them in Promises to satisfy the async interface. If you are doing massive batch writes (10k+ keys), be aware it will briefly block the event loop.
  3. LevelDB TTLs: LevelDB lacks native TTL support. We use a "shadow key" pattern (__meta__${key}) to store expiration timestamps. Expired keys are lazily cleaned up on read.
  4. Cloudflare clear(): CF KV does not support bulk deletes natively without listing keys first (which is rate-limited and expensive). Calling clear() on the CF adapter throws an error by design to prevent accidental production blowups.
  5. Vercel/Upstash Binary: Upstash's REST API expects strings. When using msgpack, we Base64 encode the payload. This adds ~33% overhead. If you only store JSON-serializable data on Vercel, switch to serializer: 'json'.

🀝 Support the Project

If @rabbx/kv saves you time or helps you ship faster, consider buying me a coffee or sponsoring the project. It keeps the lights on and the dependencies updated.


πŸ“„ License

MIT Β© rabbxdev

About

a crossplatform key value strore.Runs on bun,node,cf,deno.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages