@envoy1084/effect-redis
v0.0.2
Published
Effect bindings for Redis
Maintainers
Readme
effect-redis
A experimental, Effect wrapper for Redis, providing type-safe, composable Redis operations with support for transactions, pipelines, and all major Redis command groups.
What is This?
effect-redis is a Redis client wrapper built on top of Effect and the redis npm package. It provides:
- Effect-based API: Full integration with Effect's functional error handling, dependency injection, and compositional patterns
- Type-safe operations: TypeScript types for all Redis commands with proper argument and return type validation
- Transaction support: Built-in support for Redis
MULTItransactions with automatic queuing - Pipeline support: Batch multiple commands efficiently with automatic execution
- Comprehensive command coverage: Support for 11+ Redis command groups including strings, hashes, lists, sets, sorted sets, geospatial, JSON, bitmaps, HyperLogLog, scripting, and generic commands
- Dependency-driven: Leverages Effect's Layer system for elegant connection management and resource lifecycle handling
- Error handling: Unified error handling through Effect's error channel with custom
RedisErrortype
Quick Start
Installation
npm install @envoy1084/effect-redis effect @effect/platform-node
# or
yarn add @envoy1084/effect-redis effect @effect/platform-node
# or
pnpm add @envoy1084/effect-redis effect @effect/platform-node
# or
bun add @envoy1084/effect-redis effect @effect/platform-nodeBasic Usage
import { runMain } from "@effect/platform-node/NodeRuntime";
import {
layerWithOptions,
RedisCore,
RedisCoreLive,
} from "@envoy1084/effect-redis";
import { Effect, Layer } from "effect";
// Create the Redis layer with connection options
const RedisLayer = RedisCoreLive.pipe(
Layer.provideMerge(
layerWithOptions({
url: "redis://localhost:6379",
}),
),
);
// Define your program
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Use redis commands
yield* redis.set("mykey", "myvalue");
const value = yield* redis.get("mykey");
yield* Effect.log(`Got value: ${value}`);
});
// Run the program
program.pipe(Effect.provide(RedisLayer), runMain);Connection Setup
Providing Layers to Your Program
effect-redis uses Effect's Layer system for dependency injection and resource management. Here's how to configure connections:
1. With Custom Connection Options
import { layerWithOptions } from "@envoy1084/effect-redis";
import { Layer } from "effect";
const RedisLayer = RedisCoreLive.pipe(
Layer.provideMerge(
layerWithOptions({
url: "redis://localhost:6379",
password: "your-password",
database: 0,
// ... all RedisClientOptions from the redis package
}),
),
);2. Multiple Redis Instances
import { RedisCore, RedisCoreLive, layerWithOptions } from "@envoy1084/effect-redis";
import { Context, Layer, Effect } from "effect";
// Define tags for different Redis instances
class PrimaryRedis extends Context.Tag("PrimaryRedis")<
PrimaryRedis,
RedisCoreShape
>() {}
class CacheRedis extends Context.Tag("CacheRedis")<
CacheRedis,
RedisCoreShape
>() {}
// Create separate layers
const PrimaryRedisLayer = Layer.effect(
PrimaryRedis,
Effect.gen(function* () {
const { client } = yield* RedisConnection;
return makeRedisCore(client);
}),
).pipe(
Layer.provideMerge(
layerWithOptions({ url: "redis://primary:6379" }),
),
);
// Use in your program
const program = Effect.gen(function* () {
const primary = yield* PrimaryRedis;
const cache = yield* CacheRedis;
// Use both instances
});Usage Guide
String Commands
String commands operate on text values.
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Basic GET/SET
yield* redis.set("name", "Alice");
const name = yield* redis.get("name");
// Multiple keys
yield* redis.mSet([
["key1", "value1"],
["key2", "value2"],
]);
const values = yield* redis.mGet(["key1", "key2"]);
// Increment/Decrement
yield* redis.set("counter", "10");
yield* redis.incr("counter"); // 11
yield* redis.incrBy("counter", 5); // 16
yield* redis.decr("counter"); // 15
yield* redis.decrBy("counter", 3); // 12
// Float operations
yield* redis.set("price", "19.99");
yield* redis.incrByFloat("price", 0.01); // 20.00
// Advanced operations
yield* redis.append("name", " Smith");
const length = yield* redis.strLen("name");
const substring = yield* redis.getRange("name", 0, 4);
const oldValue = yield* redis.getSet("name", "Bob");
yield* redis.setNX("newkey", "value"); // Only if key doesn't exist
yield* redis.setEx("tempkey", 60, "value"); // With expiration
});Supported String Commands:
get,set,mGet,mSet,setNX,mSetNXincr,incrBy,incrByFloat,decr,decrByappend,strLen,getRange,setRangegetSet,getDel,getEx,setEx,pSetExgetdel,delEx,digest,lcs,mSetEx
Hash Commands
Hash commands operate on objects with named fields.
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Basic operations
yield* redis.hSet("user:1", "name", "Alice");
yield* redis.hSet("user:1", {
"age": "30",
"email": "[email protected]",
});
const name = yield* redis.hGet("user:1", "name");
const allFields = yield* redis.hGetAll("user:1");
const keys = yield* redis.hKeys("user:1");
const values = yield* redis.hVals("user:1");
// Check existence
const exists = yield* redis.hExists("user:1", "name");
// Increment
yield* redis.hSet("user:1", "age", "30");
yield* redis.hIncrBy("user:1", "age", 1); // 31
yield* redis.hIncrByFloat("user:1", "score", 2.5);
// Multiple operations
const fields = yield* redis.hmGet("user:1", ["name", "email"]);
// Delete and set operations
yield* redis.hSetNX("user:1", "name", "Bob"); // Won't update
yield* redis.hDel("user:1", "email");
// Expiration (Redis 7.4+)
yield* redis.hExpire("user:1", ["name", "email"], 3600);
const ttl = yield* redis.hTTL("user:1", "name");
});Supported Hash Commands:
hGet,hSet,hGetAll,hmGet,hKeys,hVals,hLenhExists,hSetNX,hDel,hGetDelhIncrBy,hIncrByFloat,hStrLenhExpire,hExpireAt,hExpireTime,hPersisthpExpire,hpExpireAt,hpExpireTime,hpTTL,hTTLhRandField,hScan,hGetEx,hSetEx
List Commands
List commands operate on ordered collections of strings.
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Push operations
yield* redis.lPush("tasks", "task1");
yield* redis.lPush("tasks", ["task2", "task3"]);
yield* redis.rPush("tasks", "task4");
// Pop operations
const firstTask = yield* redis.lPop("tasks");
const lastTask = yield* redis.rPop("tasks");
// Get range
const allTasks = yield* redis.lRange("tasks", 0, -1);
const midTasks = yield* redis.lRange("tasks", 0, 5);
// Check length
const count = yield* redis.lLen("tasks");
// Index operations
const taskByIndex = yield* redis.lIndex("tasks", 2);
yield* redis.lSet("tasks", 0, "updated_task");
// Insert and remove
yield* redis.lInsert("tasks", "BEFORE", "task2", "new_task");
yield* redis.lRem("tasks", 1, "old_task");
yield* redis.lTrim("tasks", 0, 10);
// Move between lists
yield* redis.lMove("tasks", "completed", "LEFT", "RIGHT");
// Blocking operations (waits for element)
const task = yield* redis.blPop(["tasks"], 30); // 30 second timeout
});Supported List Commands:
lPush,lPushX,rPush,rPushXlPop,rPop,lLen,lIndex,lSetlRange,lRem,lTrim,lInsert,lPoslMove,lMPop,rPopLPushblPop,brPop,brPopLPush,blMove,blmPop
Set Commands
Set commands operate on unordered collections of unique strings.
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Add and check members
yield* redis.sAdd("tags", ["javascript", "typescript", "nodejs"]);
const isMember = yield* redis.sIsMember("tags", "javascript");
const allMembers = yield* redis.sMembers("tags");
const count = yield* redis.sCard("tags");
// Multiple membership check
const members = yield* redis.smIsMember("tags", ["javascript", "python"]);
// Random members
const randomMember = yield* redis.sRandMember("tags");
// Pop operations
const popped = yield* redis.sPop("tags");
// Set operations
const intersection = yield* redis.sInter(["tags", "languages"]);
const union = yield* redis.sUnion(["tags", "languages"]);
const difference = yield* redis.sDiff(["tags", "languages"]);
// Store operations
yield* redis.sInterStore("common_tags", ["tags", "languages"]);
yield* redis.sUnionStore("all_tags", ["tags", "languages"]);
yield* redis.sDiffStore("unique_tags", ["tags", "languages"]);
// Remove
yield* redis.sRem("tags", "python");
// Move
yield* redis.sMove("tags", "languages", "javascript");
// Scan
const scanResult = yield* redis.sScan("tags", "0");
});Supported Set Commands:
sAdd,sRem,sCard,sMembers,sPop,sRandMembersIsMember,smIsMember,sMovesInter,sInterCard,sInterStoresUnion,sUnionStoresDiff,sDiffStoresScan
Sorted Set Commands
Sorted set commands operate on sets with scores that determine order.
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Reset for deterministic output
yield* redis.del("leaderboard");
yield* redis.del("active_players");
yield* redis.del("leaderboard");
// Seed data
yield* redis.zAdd("leaderboard", [
{ score: 100, value: "Alice" },
{ score: 90, value: "Bob" },
{ score: 85, value: "Charlie" },
]);
yield* redis.zAdd("active_players", [
{ score: 1, value: "Alice" },
{ score: 1, value: "Bob" },
]);
// Reads
const score = yield* redis.zScore("leaderboard", "Alice");
const scores = yield* redis.zmScore("leaderboard", ["Alice", "Bob"]);
const rank = yield* redis.zRank("leaderboard", "Alice");
const revRank = yield* redis.zRevRank("leaderboard", "Alice");
const total = yield* redis.zCard("leaderboard");
const inRange = yield* redis.zCount("leaderboard", 80, 100);
// Ranges
const topThreeByRank = yield* redis.zRange("leaderboard", 0, 2);
const topThreeByScore = yield* redis.zRangeByScore("leaderboard", 85, 100);
const lexRange = yield* redis.zRangeByLex("leaderboard", "-", "+");
// Set ops
const inter = yield* redis.zInter(["leaderboard", "active_players"]);
const union = yield* redis.zUnion(["leaderboard", "active_players"]);
const scanResult = yield* redis.zScan("leaderboard", "0");
// Mutations LAST
const highest = yield* redis.zPopMax("leaderboard");
const lowest = yield* redis.zPopMin("leaderboard");
});Supported Sorted Set Commands:
zAdd,zRem,zCard,zCount,zScore,zmScorezRank,zRevRank,zRange,zRevRangezRangeByScore,zRangeByLex,zRangeStorezPopMax,zPopMin,zMPopzIncrBy,zRandMemberzRemRangeByRank,zRemRangeByScore,zRemRangeByLexzLexCount,zInter,zInterCard,zInterStorezUnion,zUnionStore,zDiff,zDiffStorezScan
Generic Commands
Generic commands work with all data types.
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
const exists = yield* redis
.exists(["key1", "key2", "key3"])
.pipe(Effect.catchTag("RedisError", () => Effect.succeed(0)));
const type = yield* redis.type("mykey");
yield* redis.del(["key1", "key2"]);
yield* redis.unlink(["key3", "key4"]); // async delete
yield* redis.expire("tempkey", 3600); // seconds
const ttl = yield* redis.ttl("tempkey");
const pttl = yield* redis.pTTL("tempkey");
yield* redis.expireAt("tempkey", Math.floor(Date.now() / 1000) + 3600);
yield* redis.pExpireAt("tempkey", Date.now() + 3_600_000);
const expirationTime = yield* redis.expireTime("tempkey");
const pExpirationTime = yield* redis.pExpireTime("tempkey");
yield* redis.persist("tempkey");
yield* redis.set("oldkey", "some value");
yield* redis.set("newkey", "some other value");
yield* redis
.rename("oldkey", "newkey")
.pipe(Effect.catchTag("RedisError", () => Effect.succeed(undefined)));
const renamed = yield* redis
.exists("oldkey")
.pipe(
Effect.flatMap((exists) =>
exists === 1 ? redis.renameNX("oldkey", "newkey") : Effect.succeed(0),
),
);
yield* redis.copy("source", "destination", { REPLACE: true });
const scanResult = yield* redis.scan("0", {
COUNT: 10,
MATCH: "user:*",
});
const userKeys = scanResult.keys;
const randomKey = yield* redis.randomKey();
const sorted = yield* redis.sort("mylist", {
ALPHA: true,
BY: "weight_*",
DIRECTION: "DESC",
GET: ["object_*", "#"],
LIMIT: { count: 10, offset: 0 },
});
const touched = yield* redis.touch(["key1", "key2"]);
const serialized = yield* redis.dump("mykey");
});Supported Generic Commands:
del,exists,expire,expireAt,expireTimekeys,migrate,move,persistpExpire,pExpireAt,pExpireTime,pTTLrandomKey,rename,renameNX,restorescan,sort,sortRo,touch,ttl,typeunlink,wait,copy,dump
JSON Commands
JSON commands store and manipulate JSON documents (requires Redis Stack).
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
yield* redis.json.set("user:1", "$", {
age: 30,
metadata: {
active: true,
created: "2024-01-01",
},
name: "Alice",
tags: ["developer", "typescript"],
});
const user = yield* redis.json.get("user:1");
const name = yield* redis.json.get("user:1", {
path: "$.name",
}); // → ["Alice"]
yield* redis.json.set("user:1", "$.age", 31);
yield* redis.json.strAppend("user:1", " Smith", {
path: "$.name",
});
const nameLength = yield* redis.json.strLen("user:1", {
path: "$.name",
});
// → [number]
yield* redis.json.numIncrBy("user:1", "$.age", 1);
yield* redis.json.numMultBy("user:1", "$.age", 2);
yield* redis.json.arrAppend("user:1", "$.tags", "nodejs");
yield* redis.json.arrInsert("user:1", "$.tags", 0, "javascript");
const tagsLength = yield* redis.json.arrLen("user:1", {
path: "$.tags",
});
// → [number]
const popped = yield* redis.json.arrPop("user:1", {
index: -1,
path: "$.tags",
}); // → ["nodejs"]
yield* redis.json.arrTrim("user:1", "$.tags", 0, 2);
const tagIndex = yield* redis.json.arrIndex("user:1", "$.tags", "typescript"); // → [number]
const objKeys = yield* redis.json.objKeys("user:1", {
path: "$.metadata",
}); // → [["active", "created"]]
const objLen = yield* redis.json.objLen("user:1", {
path: "$.metadata",
}); // → [number]
const valueType = yield* redis.json.type("user:1", {
path: "$",
});
// → ["object"];
yield* redis.json.toggle("user:1", "$.metadata.active");
yield* redis.json.del("user:1", {
path: "$.metadata.active",
});
const users = yield* redis.json.mGet(["user:1", "user:2", "user:3"], "$");
// → Array<JSON[] | null>
yield* redis.json.mSet([
{ key: "user:4", path: "$", value: { name: "Bob" } },
{ key: "user:5", path: "$", value: { name: "Charlie" } },
]);
yield* redis.json.clear("user:1", {
path: "$.tags",
});
yield* redis.json.clear("user:1", {
path: "$.metadata",
});
const memoryUsage = yield* redis.json.debugMemory("user:1");
});Supported JSON Commands:
get,set,del,clear,merge,forgetmGet,mSettype,debugMemorystrAppend,strLennumIncrBy,numMultByarrAppend,arrIndex,arrInsert,arrLen,arrPop,arrTrimobjKeys,objLentoggle
Geospatial Commands
Geospatial commands store and query geographic coordinates.
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Add locations
yield* redis.geoAdd("cities", [
{
latitude: 37.775,
longitude: -122.419,
member: "San Francisco",
},
{
latitude: 34.052,
longitude: -118.243,
member: "Los Angeles",
},
{ latitude: 41.878, longitude: -87.629, member: "Chicago" },
]);
// Get positions
const positions = yield* redis.geoPos("cities", [
"San Francisco",
"Los Angeles",
]);
// Get distance
const distance = yield* redis.geoDist(
"cities",
"San Francisco",
"Los Angeles",
"km",
);
// Get geohash
const hashes = yield* redis.geoHash("cities", [
"San Francisco",
"Los Angeles",
]);
// Find nearby locations
const nearby = yield* redis.geoRadius(
"cities",
{
latitude: 37.775,
longitude: -122.419,
},
100,
"km",
);
// Find nearby from member
const nearbyFromMember = yield* redis.geoRadiusByMember(
"cities",
"San Francisco",
100,
"km",
);
// Search in box/circle (Redis 6.2+)
const searchBox = yield* redis.geoSearch("cities", "San Francisco", {
height: 200,
unit: "km",
width: 200,
});
const searchCircle = yield* redis.geoSearch("cities", "San Francisco", {
radius: 100,
unit: "km",
});
// Store search results
yield* redis.geoSearchStore("nearby", "cities", "San Francisco", {
radius: 100,
unit: "km",
});
});Supported Geospatial Commands:
geoAdd,geoDist,geoHash,geoPosgeoRadius,geoRadiusByMembergeoRadiusRo,geoRadiusByMemberRogeoSearch,geoSearchStore
Bitmap Commands
Bitmap commands perform bitwise operations on strings.
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Set and get bits
yield* redis.setBit("bitmap", 7, 1);
const bit = yield* redis.getBit("bitmap", 7);
// Count set bits
const count = yield* redis.bitCount("bitmap");
const countRange = yield* redis.bitCount("bitmap", { end: 10, start: 0 });
// Find first set/clear bit
const firstSet = yield* redis.bitPos("bitmap", 1);
const firstClear = yield* redis.bitPos("bitmap", 0);
const firstSetAfter = yield* redis.bitPos("bitmap", 1, 5);
// Bitwise operations
yield* redis.bitOp("AND", "dest", ["bitmap1", "bitmap2"]);
yield* redis.bitOp("OR", "dest", ["bitmap1", "bitmap2"]);
yield* redis.bitOp("XOR", "dest", ["bitmap1", "bitmap2"]);
yield* redis.bitOp("NOT", "dest", ["bitmap1"]);
// Bitfield operations
const fieldResult = yield* redis.bitField("mykey", [
{ encoding: "u4", offset: 0, operation: "GET" },
{ encoding: "u4", offset: 4, operation: "SET", value: 15 },
]);
});Supported Bitmap Commands:
getBit,setBit,bitCount,bitPosbitOp,bitField,bitFieldRo
HyperLogLog Commands
HyperLogLog commands provide probabilistic cardinality estimation (count unique elements).
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Add elements
yield* redis.pfAdd("hll", ["a", "b", "c", "d", "e"]);
yield* redis.pfAdd("hll", ["f", "g"]);
// Get cardinality (approximate count of unique elements)
const count = yield* redis.pfCount(["hll"]);
// Count across multiple HyperLogLogs
const multiCount = yield* redis.pfCount(["hll1", "hll2", "hll3"]);
// Merge HyperLogLogs
yield* redis.pfMerge("combined", ["hll1", "hll2", "hll3"]);
const mergedCount = yield* redis.pfCount(["combined"]);
});Supported HyperLogLog Commands:
pfAdd,pfCount,pfMerge
Scripting Commands
Scripting commands allow server-side Lua script execution.
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
yield* redis.set("mykey", "42");
yield* redis.set("counter", "8");
// Execute Lua script
const result = yield* redis.eval("return redis.call('GET',KEYS[1])", {
arguments: [],
keys: ["mykey"],
});
// Script with arguments
const sum = yield* redis.eval(
`
local val1 = redis.call('GET', KEYS[1])
local val2 = tonumber(ARGV[1])
return val1 + val2
`,
{
arguments: ["10"],
keys: ["counter"],
},
);
// Load script (returns SHA1)
const sha = yield* redis.scriptLoad("return 'Hello Redis'");
// Execute by SHA
const cachedResult = yield* redis.evalSha(sha, {
arguments: [],
keys: [],
});
// Check if scripts exist
const exists = yield* redis.scriptExists([sha]);
// Flush script cache
yield* redis.scriptFlush();
});Supported Scripting Commands:
eval,evalSha,evalRo,evalShaRoscriptDebug,scriptExists,scriptFlush,scriptKill,scriptLoadfCall,fCallRofunctionDelete,functionDump,functionFlush,functionKillfunctionList,functionLoad,functionRestore,functionStats
Transactions (MULTI/EXEC)
Transactions allow you to batch multiple commands and execute them atomically.
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Start a transaction
const [result, execResult] = yield* redis.multi((tx) =>
Effect.gen(function* () {
yield* tx.set("key1", "value1");
yield* tx.set("key2", "value2");
const val = yield* tx.get("key1");
yield* tx.incr("counter");
return val; // This is the "result" returned
}),
);
// result contains the return value from the transaction block
yield* Effect.log(`Transaction result: ${result}`);
// execResult is an array of responses from each command
// null if transaction was aborted
yield* Effect.log(`Exec responses: ${execResult}`);
});Pipelines
Pipelines batch multiple commands for more efficient execution (like transactions but without atomicity guarantees).
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Execute multiple commands in a pipeline
const [result, execResult] = yield* redis.pipeline((pipe) =>
Effect.gen(function* () {
yield* pipe.set("key1", "value1");
yield* pipe.set("key2", "value2");
yield* pipe.set("key3", "value3");
const val = yield* pipe.get("key1");
yield* pipe.mGet(["key1", "key2", "key3"]);
return val;
}),
);
// result contains the return value from the pipeline block
yield* Effect.log(`Pipeline result: ${result}`);
// execResult is an array of responses from each command
yield* Effect.log("Pipeline responses:", execResult);
// Pipeline with many operations
const [bulkResult, responses] = yield* redis.pipeline((pipe) =>
Effect.gen(function* () {
// Simulate bulk data operations
for (let i = 0; i < 1000; i++) {
yield* pipe.set(`key:${i}`, `value:${i}`);
}
return "bulk operation complete";
}),
);
yield* Effect.log("Pipeline responses:", responses);
yield* Effect.log("Pipeline result:", bulkResult);
});Complete Command Reference
String Commands (24)
append, decr, decrBy, delEx, digest, get, getDel, getEx, getRange
getSet, incr, incrBy, incrByFloat, lcs, mGet, mSet, mSetEx, mSetNX
pSetEx, set, setEx, setNX, setRange, strLenHash Commands (25)
hDel, hExists, hExpire, hExpireAt, hExpireTime, hGet, hGetAll, hGetDel
hGetEx, hIncrBy, hIncrByFloat, hKeys, hLen, hmGet, hPersist, hpExpire
hpExpireAt, hpExpireTime, hpTTL, hRandField, hScan, hSet, hSetEx
hSetNX, hStrLen, hTTL, hValsList Commands (21)
blMove, blmPop, blPop, brPop, brPopLPush, lIndex, lInsert, lLen, lMove
lPop, lPos, lPush, lPushX, lRange, lRem, lSet, lTrim, rPop
rPopLPush, rPush, rPushXSet Commands (17)
sAdd, sCard, sDiff, sDiffStore, sInter, sInterCard, sInterStore, sIsMember
sMembers, smIsMember, sMove, sPop, sRandMember, sRem, sScan, sUnion
sUnionStoreSorted Set Commands (34)
bzMPop, bzPopMax, bzPopMin, zAdd, zCard, zCount, zDiff, zDiffStore
zIncrBy, zInter, zInterCard, zInterStore, zLexCount, zmPop, zmScore
zPopMax, zPopMin, zRandMember, zRange, zRangeByLex, zRangeByScore
zRangeStore, zRank, zRem, zRemRangeByLex, zRemRangeByRank
zRemRangeByScore, zRevRank, zScan, zScore, zUnion, zUnionStoreGeneric Commands (30)
copy, del, dump, exists, expire, expireAt, expireTime, keys, migrate
move, persist, pExpire, pExpireAt, pExpireTime, pTTL, randomKey
rename, renameNX, restore, scan, sort, sortRo, touch, ttl, type
unlink, waitJSON Commands (24)
arrAppend, arrIndex, arrInsert, arrLen, arrPop, arrTrim, clear, debugMemory
del, forget, get, merge, mGet, mSet, numIncrBy, numMultBy, objKeys
objLen, set, strAppend, strLen, toggle, typeGeospatial Commands (10)
geoAdd, geoDist, geoHash, geoPos, geoRadius, geoRadiusByMember
geoRadiusByMemberRo, geoRadiusRo, geoSearch, geoSearchStoreBitmap Commands (7)
bitCount, bitField, bitFieldRo, bitOp, bitPos, getBit, setBitHyperLogLog Commands (3)
pfAdd, pfCount, pfMergeScripting Commands (19)
eval, evalRo, evalSha, evalShaRo, fCall, fCallRo, functionDelete
functionDump, functionFlush, functionKill, functionList, functionLoad
functionRestore, functionStats, scriptDebug, scriptExists, scriptFlush
scriptKill, scriptLoadError Handling
effect-redis uses Effect's error handling system with a custom RedisError type:
import { Effect } from "effect";
import { RedisError } from "@envoy1084/effect-redis";
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Errors are automatically caught and wrapped in RedisError
const result = yield* redis.get("mykey").pipe(
Effect.catchTag("RedisError", (error) => {
yield* Effect.log(`Redis error: ${error.message}`);
return Effect.succeed("default_value");
}),
);
});Advanced Usage
Combining Commands with Effect
import { Effect, Array as EffectArray } from "effect";
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
// Process multiple keys in parallel
const keys = ["user:1", "user:2", "user:3"];
const users = yield* Effect.all(
keys.map((key) => redis.get(key)),
{ concurrency: 3 },
);
// Conditional operations
const exists = yield* redis.exists(["mykey"]);
if (exists > 0) {
const value = yield* redis.get("mykey");
yield* Effect.log(value);
}
// Error recovery
const value = yield* redis
.get("mayNotExist")
.pipe(
Effect.orElse(() => Effect.succeed("default")),
);
});Custom Context for Dependency Injection
import { Context, Effect, Layer } from "effect";
class MyService extends Context.Tag("MyService")<
MyService,
{ doSomething: () => Effect.Effect<string> }
>() {}
const myServiceLive = Layer.succeed(MyService, {
doSomething: () => Effect.succeed("done"),
});
const program = Effect.gen(function* () {
const redis = yield* RedisCore;
const service = yield* MyService;
const result = yield* service.doSomething();
yield* redis.set("result", result);
});
// Compose layers
const AppLayer = Layer.mergeAll(RedisLayer, myServiceLive);
program.pipe(Effect.provide(AppLayer), runMain);Future Work
Following command groups are supported:
- [x] String commands
- [x] Hash commands
- [x] List commands
- [x] Set commands
- [x] Sorted set commands
- [ ] Stream commands
- [x] Bitmap commands
- [x] HyperLogLog commands
- [x] Geospatial commands
- [x] JSON commands
- [ ] Search commands
- [ ] Time series commands
- [ ] Vector set commands
- [ ] Pub/Sub commands
- [x] Transaction commands (Pipeline and Multi)
- [x] Scripting commands
- [ ] Connection commands
- [ ] Server commands
- [ ] Cluster commands
- [x] Generic commands
We are working on adding support for other Redis command groups. If you have any suggestions or requests, please feel free to open an issue or submit a pull request.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT
