@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
Markdown
# /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 /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.