UNPKG

@digicroz/node-backend-utils

Version:

Backend utilities for Node.js applications - Redis client wrappers and more utilities for TypeScript/JavaScript projects

328 lines (231 loc) 8.56 kB
# @digicroz/node-backend-utils Backend utilities for Node.js applications with TypeScript support. This package provides robust, production-ready utilities for common backend operations. ## Features - 🚀 **Redis Client Wrapper** - Simplified Redis operations with automatic connection management - 🔒 **Type-Safe** - Full TypeScript support with comprehensive type definitions - 🌳 **Tree-Shakeable** - Import only what you need - 📦 **ESM & CommonJS** - Supports both module systems - ⚡ **Production Ready** - Battle-tested utilities with graceful error handling ## Installation ```bash npm install @digicroz/node-backend-utils ``` ## Redis Utilities ### Quick Start ```typescript import { redisBase, createRedisClient } from "@digicroz/node-backend-utils" // Initialize Redis connection await redisBase.initialize() // Create a client with namespace prefix const userCache = createRedisClient("user", true) // Use the client await userCache.set("john", "John Doe") const user = await userCache.get("john") ``` ### Redis Base Client The base singleton Redis client with automatic connection management: ```typescript import { redisBase } from "@digicroz/node-backend-utils/redis" // Initialize (call once at app startup) await redisBase.initialize() // Check connection status const isConnected = redisBase.isClientConnected() // Get client for advanced operations const client = redisBase.getClient() // Safe execution with error handling const result = await redisBase.safeExecute(async (client) => { return await client.get("mykey") }) // Disconnect when shutting down await redisBase.disconnect() ``` **Environment Variables:** - `REDIS_URL` - Redis connection URL (e.g., `redis://localhost:6379`) ### Redis Generic Client A feature-rich Redis client with namespace prefixing and type-safe object storage: ```typescript import { createRedisClient } from "@digicroz/node-backend-utils/redis" // Create a namespaced client const cache = createRedisClient("myapp", true) // String operations await cache.set("key", "value") await cache.setEx("key", 3600, "value") // with expiration const value = await cache.get("key") // Object operations (automatic JSON serialization) await cache.setObj("user:1", { name: "John", age: 30 }) await cache.setObjEx("user:2", 3600, { name: "Jane", age: 25 }) const user = await cache.getObj<User>("user:1") // Key operations await cache.del("key") await cache.delMultiple(["key1", "key2"]) await cache.exists("key") await cache.expire("key", 3600) await cache.ttl("key") // Counter operations await cache.incr("counter") await cache.incrBy("counter", 5) await cache.decr("counter") // Pattern matching const keys = await cache.keys("user:*") // Hash operations await cache.hSet("hash", "field", "value") await cache.hGet("hash", "field") await cache.hGetAll("hash") await cache.hDel("hash", "field") // List operations await cache.lPush("list", "item1", "item2") await cache.rPush("list", "item3") await cache.lPop("list") await cache.rPop("list") await cache.lRange("list", 0, -1) // Set operations await cache.sAdd("set", "member1", "member2") await cache.sMembers("set") await cache.sRem("set", "member1") // Custom operations with prefix support const result = await cache.safeExecute(async (client, addPrefix) => { const key = addPrefix("mykey") return await client.get(key) }) ``` **Features:** - ✅ Automatic namespace prefixing - ✅ Object serialization/deserialization - ✅ Graceful error handling (returns null on errors) - ✅ Enable/disable without code changes - ✅ Full TypeScript support ## Usage Examples ### Basic Setup ```typescript // app.ts import { redisBase } from "@digicroz/node-backend-utils" async function startServer() { // Initialize Redis at startup await redisBase.initialize() // Your app initialization... } startServer() ``` ### User Session Cache ```typescript import { createRedisClient } from "@digicroz/node-backend-utils" const sessionCache = createRedisClient("session", true) interface Session { userId: string email: string loginTime: number } // Store session with 1 hour expiration async function createSession(sessionId: string, userId: string, email: string) { const session: Session = { userId, email, loginTime: Date.now(), } await sessionCache.setObjEx(sessionId, 3600, session) } // Get session async function getSession(sessionId: string) { return await sessionCache.getObj<Session>(sessionId) } ``` ### Rate Limiting ```typescript import { createRedisClient } from "@digicroz/node-backend-utils" const rateLimitCache = createRedisClient("ratelimit", true) async function checkRateLimit(userId: string, maxRequests: number = 100) { const key = `${userId}:requests` const count = await rateLimitCache.incr(key) if (count === 1) { // First request, set expiration to 1 minute await rateLimitCache.expire(key, 60) } return count && count <= maxRequests } ``` ### Feature Flags ```typescript import { createRedisClient } from "@digicroz/node-backend-utils" const featureFlags = createRedisClient("feature", true) interface FeatureFlag { enabled: boolean rolloutPercentage: number enabledUsers?: string[] } async function isFeatureEnabled(featureName: string, userId: string) { const flag = await featureFlags.getObj<FeatureFlag>(featureName) if (!flag || !flag.enabled) return false if (flag.enabledUsers?.includes(userId)) return true // Implement percentage-based rollout const hash = userId .split("") .reduce((acc, char) => acc + char.charCodeAt(0), 0) return hash % 100 < flag.rolloutPercentage } ``` ## API Reference ### redisBase - `initialize()` - Initialize Redis connection - `getClient()` - Get the raw Redis client - `isClientConnected()` - Check connection status - `disconnect()` - Close the connection - `safeExecute(operation)` - Execute operation with error handling - `getStatus()` - Get connection status details ### createRedisClient(prefix, isEnabled) Creates a new Redis client instance with the given prefix. **Parameters:** - `prefix` - Namespace prefix for all keys - `isEnabled` - Enable/disable Redis operations (default: true) **Returns:** RedisGenericClient instance ### RedisGenericClient Methods All methods return `null` if the client is disabled or on error. #### String Operations - `set(key, value)` - Set a string value - `setEx(key, seconds, value)` - Set with expiration - `get(key)` - Get a string value #### Object Operations - `setObj<T>(key, value)` - Set an object (auto-serialized) - `setObjEx<T>(key, seconds, value)` - Set object with expiration - `getObj<T>(key)` - Get an object (auto-deserialized) #### Key Operations - `del(key)` - Delete a key - `delMultiple(keys)` - Delete multiple keys - `exists(key)` - Check if key exists - `expire(key, seconds)` - Set expiration - `ttl(key)` - Get time to live - `keys(pattern)` - Find keys by pattern #### Counter Operations - `incr(key)` - Increment by 1 - `incrBy(key, amount)` - Increment by amount - `decr(key)` - Decrement by 1 #### Hash Operations - `hSet(key, field, value)` - Set hash field - `hGet(key, field)` - Get hash field - `hGetAll(key)` - Get all hash fields - `hDel(key, fields)` - Delete hash fields #### List Operations - `lPush(key, ...elements)` - Push to left - `rPush(key, ...elements)` - Push to right - `lPop(key)` - Pop from left - `rPop(key)` - Pop from right - `lRange(key, start, stop)` - Get range #### Set Operations - `sAdd(key, ...members)` - Add members - `sMembers(key)` - Get all members - `sRem(key, ...members)` - Remove members ## Environment Variables ```env REDIS_URL=redis://localhost:6379 ``` ## Best Practices 1. **Initialize Once** - Call `redisBase.initialize()` at application startup 2. **Use Prefixes** - Use meaningful prefixes to organize your keys 3. **Set Expirations** - Always set TTL for cache data 4. **Handle Nulls** - All methods can return `null`, always check results 5. **Graceful Degradation** - The client is designed to fail gracefully ## License MIT ## Author Adarsh Hatkar - [GitHub](https://github.com/AdarshHatkar) ## Contributing Contributions are welcome! Please feel free to submit a Pull Request.