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