@unikvs/redis.bun
Learn how to use the storage plugin that saves byte sequences to Redis with Bun's Redis client.
Overview
@unikvs/redis.bun is a storage plugin that saves data to Redis using Bun’s built-in Redis client. Keys are namespaced with a prefix.Bun only
It handles byte sequences only. write accepts a Uint8Array<ArrayBuffer> and read returns a Uint8Array<ArrayBuffer>. Bytes are stored as-is in Redis string values and fetched with getBuffer. Connections use the RedisClient from the bun module via dynamic import. Redis server 7.2 or later is required.
Install it as follows.
npm install @unikvs/redis.bunpnpm add @unikvs/redis.bunyarn add @unikvs/redis.bunbun add @unikvs/redis.bunnub add @unikvs/redis.bunaube add @unikvs/redis.bunIt depends on @unikvs/core.
Usage
The central class is Redis.
import { Redis } from "@unikvs/redis.bun";
public constructor(url?: string, options?: RedisStorageOptions)
url is the connection target. When omitted, Bun resolves it from REDIS_URL, VALKEY_URL, and then redis://localhost:6379, in that order.
options is RedisOptions plus keyPrefix and allowRepair.
keyPrefixis a string prepended to every key. The default is"unikvs:", which limits whatclear()deletes to keys under this prefix. An empty string uses keys as-is.allowRepaircontrols whether repair writes are allowed. The default isfalse. When set tofalse, repairwriteandgetWritablecalls fail withRepairNotAllowedError.- All other properties are passed to Bun’s
RedisClient, such asconnectionTimeout,autoReconnect,maxRetries, andtls.
The main members are as follows.
nameis"Redis".isOpenindicates whether the storage is open.open()creates the client and establishes the connection.close()closes the connection.write,read,exists,delete, andclearhandle data.
import { Redis } from "@unikvs/redis.bun";
const storage = new Redis("redis://localhost:6379", { keyPrefix: "myapp:" });
await storage.open();
await storage.write({
key: "hello.bin",
data: new TextEncoder().encode("hello"),
});
const data = await storage.read({ key: "hello.bin" });
console.log(new TextDecoder().decode(data));
storage.close();
Key Layout
With the default prefix, the key hello.bin becomes the Redis key unikvs:hello.bin. No conversion or escaping beyond the prefix is applied.
unikvs:hello.bin
unikvs:greeting.bin
- Keys are built with
`${this.keyPrefix}${key}`. - Redis keys are binary-safe, so unlike
@unikvs/fs.bun, keys containing slashes, spaces, or:work as-is. clear()usesSCANto enumerate keys matching`${this.keyPrefix}*`and deletes them withDELin batches of 1000. When the prefix is empty, the pattern is"*".- The temporary key for
getWritableis`${dest}.${Bun.randomUUIDv7()}.tmp`. It lives under the same prefix, soclear()also removes temporary keys left behind by aborts.
Streams
Both readable and writable streams are supported.
Obtain a writable stream with the following method.
public getWritable(
args: Pick<IStorage.GetWritableArgs, "key"> & { signal?: AbortSignal },
): WritableStream<Uint8Array<ArrayBuffer>>
It creates an empty temporary key on start and appends each chunk with APPEND. It uses swap-on-close: data is renamed to the final key with RENAME on successful close. On failure or abort, the temporary key is removed, preserving existing data. RENAME is atomic, so readers never observe partially written contents. Concurrent writes to the same key don’t collide thanks to unique temporary keys, and the last completed RENAME wins.
Obtain a readable stream with the following method.
public getReadable(
args: Pick<IStorage.GetReadableArgs, "key" | "signal">,
): ReadableStream<Uint8Array<ArrayBuffer>>
Redis GET returns the whole value at once, so the bytes from getBuffer are emitted as a single chunk.
Notes
- Bun only.
open()throwsUnsupportedRuntimeErroron runtimes without theBunglobal. - Redis server 7.2 or later is required.
- Don’t call
writeorreadbeforeopen(). The internal connection isnulluntil then. Check withisOpen. open()waits until the connection is established before settingisOpento true. If connecting fails,isOpenstays false and the error propagates.- Calling
open()while already open closes the old connection and opens a new one. - Calling
close()a second time throws aTypeError, as do operations afterclose. write,read,exists,delete, andclearreject when given an already abortedAbortSignal. Redis commands cannot be cancelled after they are sent, so in-flight commands are not interrupted.readthrowsKeyNotFoundErrorfor missing keys.getReadablesurfaces the same error when the stream is read.deletedoes not error for missing keys.existsreturns the boolean converted by Bun’s client.readandgetReadablenormalize theBufferreturned by Bun into aUint8Array.- Persistence follows the Redis server configuration. Servers with RDB and AOF disabled lose data on restart.
Errors
| Error | Condition |
|---|---|
UnsupportedRuntimeError |
Thrown by open() on runtimes without the Bun global. A shared error from @unikvs/core. |
KeyNotFoundError |
Thrown when reading a missing key with read or getReadable. A shared error from @unikvs/core. |
import { KeyNotFoundError, Redis, UnsupportedRuntimeError } from "@unikvs/redis.bun";
const storage = new Redis();
try {
await storage.open();
await storage.read({ key: "missing.bin" });
} catch (error) {
if (error instanceof UnsupportedRuntimeError) {
console.error("Run this on the Bun runtime");
} else if (error instanceof KeyNotFoundError) {
console.error(`Key ${error.meta.key} was not found`);
} else {
throw error;
}
}
Connection, authentication, and server-side rejections propagate the errors thrown by Bun’s client. You can distinguish them by code, such as ERR_REDIS_CONNECTION_CLOSED or ERR_REDIS_SERVER_ERROR.
Examples
A minimal example storing to redis://localhost:6379.
import { Redis } from "@unikvs/redis.bun";
const storage = new Redis("redis://localhost:6379", { keyPrefix: "unikvs-example:" });
await storage.open();
const key = "greeting.bin";
await storage.write({
key,
data: new TextEncoder().encode("hello, Redis"),
});
if (await storage.exists({ key })) {
const data = await storage.read({ key });
console.log(new TextDecoder().decode(data));
}
const writer = storage.getWritable({ key: "stream.bin" }).getWriter();
await writer.write(new Uint8Array([1, 2, 3]));
await writer.write(new Uint8Array([4, 5, 6]));
await writer.close();
await storage.delete({ key });
await storage.clear();
storage.close();