Skip to content
UniKVS
English
Esc
↑↓navigate↵open⌘Jpreview
On this page

@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.bun
pnpm add @unikvs/redis.bun
yarn add @unikvs/redis.bun
bun add @unikvs/redis.bun
nub add @unikvs/redis.bun
aube add @unikvs/redis.bun

It 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.

  • keyPrefix is a string prepended to every key. The default is "unikvs:", which limits what clear() deletes to keys under this prefix. An empty string uses keys as-is.
  • allowRepair controls whether repair writes are allowed. The default is false. When set to false, repair write and getWritable calls fail with RepairNotAllowedError.
  • All other properties are passed to Bun’s RedisClient, such as connectionTimeout, autoReconnect, maxRetries, and tls.

The main members are as follows.

  • name is "Redis".
  • isOpen indicates whether the storage is open.
  • open() creates the client and establishes the connection.
  • close() closes the connection.
  • write, read, exists, delete, and clear handle 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() uses SCAN to enumerate keys matching `${this.keyPrefix}*` and deletes them with DEL in batches of 1000. When the prefix is empty, the pattern is "*".
  • The temporary key for getWritable is `${dest}.${Bun.randomUUIDv7()}.tmp`. It lives under the same prefix, so clear() 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() throws UnsupportedRuntimeError on runtimes without the Bun global.
  • Redis server 7.2 or later is required.
  • Don’t call write or read before open(). The internal connection is null until then. Check with isOpen.
  • open() waits until the connection is established before setting isOpen to true. If connecting fails, isOpen stays 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 a TypeError, as do operations after close.
  • write, read, exists, delete, and clear reject when given an already aborted AbortSignal. Redis commands cannot be cancelled after they are sent, so in-flight commands are not interrupted.
  • read throws KeyNotFoundError for missing keys. getReadable surfaces the same error when the stream is read.
  • delete does not error for missing keys.
  • exists returns the boolean converted by Bun’s client.
  • read and getReadable normalize the Buffer returned by Bun into a Uint8Array.
  • 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();

Was this page helpful?