@6thbridge/utils Documentation
A comprehensive TypeScript utility library for Node.js applications, providing reusable components for common development tasks including database operations, caching, messaging, logging, and more.
Table of Contents
- Installation
- Quick Start
- Partner API keys and routes
- Core Modules
- API Reference
- Examples
- Testing
- Contributing
- License
Installation
# Using npm
npm install @6thbridge/utils
# Using yarn
yarn add @6thbridge/utils
Dependencies
This package includes the following key dependencies:
- Database:
mongoose, ,ioredismongoose-paginate-v2 - Messaging:
amqplib, ,kafkajsamqp-connection-manager - Utilities:
lodash,dayjs,joi,axios - Monitoring: ,
rollbar@opentelemetry/auto-instrumentations-node
Quick Start
import {
HelperUtil,
ReferenceUtil,
RedisClient,
RabbitMQClient,
MongoDBRepository,
logger
} from '@6thbridge/utils';
// Generate a reference
const reference = ReferenceUtil.generateReference('production');
// Use Redis
const redis = RedisClient.getInstance();
await redis.connect({ url: 'redis://localhost:6379' });
// Clean data
const cleanData = HelperUtil.removeFieldsWithEmptyValue(data);
// Log information
logger.info('Application started', { reference });
Partner API keys and routes
See Partner API keys and routes for
can(['partner'], []), the user-service key validation contract, PartnerContextEntity,
and Express route registration examples.
Core Modules
🔧 Utilities
Essential utility functions for data manipulation, reference generation, and common operations.
- HelperUtil: Data cleaning, activity logging, async utilities
- ReferenceUtil: Reference and UUID generation
- Phone Number Formatting: International phone number formatting
🗄️ Database
Database abstraction layers and repository patterns.
- MongoDBRepository: MongoDB operations with Mongoose
- Redis Integration: Caching and session management
📨 Brokers
Message broker implementations for event-driven architecture.
- RabbitMQClient: AMQP messaging with RabbitMQ
- KafkaJS Integration: Apache Kafka message streaming
💾 Cache
Caching solutions for improved performance.
- RedisClient: Redis-based caching with clustering support
- Node-Cache: In-memory caching for lightweight applications
📝 Logger
Comprehensive logging system with multiple levels and integrations.
- Structured Logging: JSON-formatted logs
- Multiple Transports: Console, file, and external service logging
- Error Tracking: Integration with monitoring services
🛡️ Middlewares
Express.js middlewares for common functionality.
- Authentication: JWT and API key validation
- Validation: Request/response validation
- Error Handling: Centralized error processing
- Rate Limiting: Redis-backed request limits with a service-owned ioredis connection
Shared rate limiter
import Redis from 'ioredis';
import { Ratelimiter } from '@6thbridge/utils';
const redisClient = new Redis(process.env.REDIS_URL);
Ratelimiter.configure({
redisClient,
serviceName: 'my-service',
});
const loginLimiter = Ratelimiter.limit({
keyPrefix: 'login',
numberOfTries: 5,
skipSuccessfulRequests: true,
});
// Use loginLimiter as Express middleware on your login route.
The default limit is five attempts per IP in a 30-minute window. Exceeding it
returns HTTP 429 with errorCode: 'RateLimitExceeded'. Set skipSuccessfulRequests
to exclude successful responses from the count; it defaults to false.
Call Ratelimiter.configure({ redisClient, serviceName }) during startup before
creating limiters. It accepts an existing standard ioredis Redis connection;
the calling service owns connection setup and shutdown. Calling limit before
configuration throws an error. Individual limit calls no longer take a client.
serviceName is optional and defaults to process.env.APP_NAME, so
Ratelimiter.configure({ redisClient }) is enough when APP_NAME is set.
An explicit serviceName takes precedence. Configuration throws if neither
provides a service name.
Every Redis key starts with rl:<serviceName>:. Set a route-specific keyPrefix
to isolate counters within a service; its default is rate-limit-key. For example,
the login limiter above uses rl:my-service:login:<normalized-ip>. Services with
different names can safely share Redis. Existing limiters retain the configuration
they were created with if configure is called again.
An optional synchronous keyGenerator(req, res) replaces the default key,
including keyPrefix, while preserving the service prefix. Without it, the
limiter uses res.locals.ip, then
cf-connecting-ip, x-forwarded-for, the ip header, or req.ip, in that order,
and normalizes IPv6 addresses. Configure trusted proxies to supply these values.
The class is also available as middlewares.Ratelimiter from the package root.
📊 Monitoring
Application monitoring and observability tools.
- OpenTelemetry: Distributed tracing
- Rollbar Integration: Error monitoring and alerting
API Reference
HelperUtil
class HelperUtil {
// Async utility
static async sleep(durationInMs: number = 1000): Promise<void>
// Data cleaning
static removeFieldsWithEmptyValue(value: any): any
// Activity logging
static async saveActivityLog(activityLog: SaveActivityLogDTO, options?: any): Promise<void>
}
ReferenceUtil
class ReferenceUtil {
// Generate environment-specific reference
static generateReference(env: string): string
// Generate reference with custom prefix
static generateReferenceWithPrefix(env: string, prefix: string): string
// Generate UUID
static generateUUID(options?: any): string
}
RedisClient
class RedisClient {
// Singleton pattern
static getInstance(): RedisClient
static resetInstance(): void
// Connection management
connect(config?: RedisConnect): Commander
// Redis operations
static getRedis(): Commander
}
RabbitMQClient
class RabbitMQClient {
// Singleton pattern
static getInstance(): RabbitMQClient
static resetInstance(): void
// Connection and messaging
connect(config: RabbitMQConnectConfig): AmqpConnectionManager
static async sendToQueue(queueName: string, payload: any, options?: PublishOptions): Promise<any>
static async publishToExchange(exchangeName: string, routeKey: string, payload: any, options?: PublishOptions): Promise<any>
// Message consumption
static listen(queueName: string, options: any, callback: Function): Promise<void>
}
MongoDBRepository
class MongoDBRepository {
constructor(Model: TypedModel<any>)
// CRUD operations
create(payload: Record<string, any>, options?: CreateOptions): Promise<any>
findById(id: string, projection?: ProjectionType<any>, options?: QueryOptions): Promise<any>
findOne(condition: Record<string, any>, sort?: Record<string, number>, options?: FindOptions): Promise<any>
find(condition: Record<string, any>, sort?: Record<string, any>, options?: FindOptions): Promise<any>
// Advanced operations
paginate(condition: Record<string, any>, sort?: Record<string, any>, options?: FindOptions): Promise<PaginateResult<any>>
updateOne(query: Record<string, any>, update: Record<string, any>, options?: MongooseOptions): Promise<any>
bulkWrite(bulkWritePayload: any): Promise<any>
}
Examples
Basic Usage Examples
Data Cleaning
import { HelperUtil } from '@6thbridge/utils';
const dirtyData = {
name: 'John Doe',
email: '',
phone: null,
address: undefined,
age: 30
};
const cleanData = HelperUtil.removeFieldsWithEmptyValue(dirtyData);
// Result: { name: 'John Doe', age: 30 }
Reference Generation
import { ReferenceUtil } from '@6thbridge/utils';
// Production reference
const prodRef = ReferenceUtil.generateReference('production');
// Example: L20240101120000ABCDE
// Development reference with prefix
const devRef = ReferenceUtil.generateReferenceWithPrefix('development', 'ORDER');
// Example: DORDER20240101120000ABCDE
Redis Operations
import { RedisClient } from '@6thbridge/utils';
async function cacheExample() {
const redis = RedisClient.getInstance();
redis.connect({
url: 'redis://localhost:6379',
keepAlive: true
});
// Cache operations
await RedisClient.getRedis().set('user:123', JSON.stringify({ name: 'John' }));
const user = await RedisClient.getRedis().get('user:123');
}
Message Queue
import { RabbitMQClient } from '@6thbridge/utils';
async function messagingExample() {
const broker = RabbitMQClient.getInstance();
broker.connect({
url: 'amqp://localhost:5672',
queues: [
{ queueName: 'user.created' }
],
exchanges: [],
exchangeBindings: []
});
// Send message
await RabbitMQClient.sendToQueue('user.created', {
userId: '123',
event: 'USER_CREATED',
timestamp: new Date()
});
// Listen for messages
RabbitMQClient.listen('user.created', {}, (message, options) => {
console.log('Received:', JSON.parse(message));
options.ack();
});
}
Phone Number Formatting
import { formatPhoneNumber, formatPhoneNumberArray } from '@6thbridge/utils';
// Single phone number
let phoneNumber = '08012345678';
phoneNumber = formatPhoneNumber(phoneNumber, 'NG');
console.log(phoneNumber); // 2348012345678
// Multiple phone numbers
let phoneNumbers = ['08012345678', '08012345679'];
phoneNumbers = formatPhoneNumberArray(phoneNumbers, 'NG');
console.log(phoneNumbers); // ['2348012345678', '2348012345679']
Logging
import { logger } from '@6thbridge/utils';
// Different log levels
logger.error('Database connection failed', { error: 'Connection refused' });
logger.warn('High memory usage detected', { usage: '85%' });
logger.info('User logged in', { userId: '123' });
logger.debug('Processing request', { requestId: 'req-456' });
// Exception logging
try {
// risky operation
} catch (error) {
logger.exception(error, { context: 'user-registration' });
}
Environment Variables
The library supports various environment variables for configuration:
# Redis Configuration
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=your_password
REDIS_URL=redis://localhost:6379
REDIS_CLUSTER_URL=redis://node1:6379,redis://node2:6379
# RabbitMQ Configuration
RABBITMQ_URL=amqp://localhost:5672
RABBITMQ_CLUSTER_URL=amqp://node1:5672,amqp://node2:5672
# Application Configuration
APP_NAME=your-app-name
TZ=Africa/Lagos
LOG_OUT=1
# Service URLs
USER_SERVICE_URL=http://user-service:3000
Testing
The library includes comprehensive test suites using Jest:
# Run all tests
yarn test
# Run tests in watch mode
yarn test --watch
# Run tests with coverage
yarn test --coverage
# Run specific test file
yarn test reference.util.test.ts
Writing Tests
import { ReferenceUtil } from '@6thbridge/utils';
describe('ReferenceUtil', () => {
it('should generate production reference', () => {
const ref = ReferenceUtil.generateReference('production');
expect(ref).toMatch(/^L\d{14}[A-Z]{5}$/);
});
});
TypeScript Support
This library is written in TypeScript and provides full type definitions:
import {
RedisConnect,
RabbitMQConnectConfig,
SaveActivityLogDTO,
MongooseOptions
} from '@6thbridge/utils';
// Type-safe configuration
const redisConfig: RedisConnect = {
url: 'redis://localhost:6379',
keepAlive: true,
heartbeat: 30
};
Performance Considerations
- Connection Pooling: Redis and RabbitMQ clients use connection pooling
- Singleton Pattern: Database and cache clients follow singleton pattern
- Lazy Loading: Connections are established only when needed
- Memory Management: Proper cleanup and connection closing
Error Handling
The library implements comprehensive error handling:
try {
await RedisClient.getRedis().set('key', 'value');
} catch (error) {
logger.exception(error, { operation: 'redis-set' });
}
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Setup
# Clone repository
git clone https://github.com/DexlerNG/bridge-utils-package.git
# Install dependencies
yarn install
# Run tests
yarn test
# Build library
yarn build
License
This project is licensed under the ISC License.
Contributors
Support
For support and questions, please open an issue on the GitHub repository. Note: This library is actively maintained and used in production environments. For the latest updates and releases, check the releases section.
Publishing
Merging a pull request into main publishes the package to npm and GitHub Packages, unless the pull request has the skip-publish label. Bump the version before merging. Add publish-test-version to a pull request from this repository to publish a test version to GitHub Packages.
Repository Actions secrets required:
NPM_TOKEN: npm token with permission to publish@6thbridge/utils.GH_PACKAGES_TOKEN: GitHub personal access token (classic) withwrite:packagespermission for the6thbridgepackage namespace. The repository currently belongs toDexlerNG, so its automaticGITHUB_TOKENcannot publish to that separate namespace.
To install the GitHub Packages copy, authenticate to npm.pkg.github.com and configure @6thbridge:registry=https://npm.pkg.github.com in your npm configuration. The normal npm install @6thbridge/utils command uses npm.
Log batching
Keep calling logger.info(), logger.error(), etc. Logs share an array and flush
at 100 messages or after 1 second. configureLogBatching({ batchSize: 100, flushIntervalMs: 1000 }) changes those defaults. Each flush sends a plain array
to the existing RabbitMQ instance, or to POST /v1/logs when none exists.
Transport selection happens during flushing. Transport failures are reported to
stderr; there is no background retry mechanism.
Call registerLoggingShutdown() once to register SIGTERM/SIGINT flushing, or
await flushLogs() explicitly before closing transports or exiting. Registration
only flushes the library buffer; it does not close providers or exit the process.
There are no automatic signal listeners when shutdown is not registered.
Deploy array support in utility-service before releasing the updated logger.
To send 10,000 test logs to your utility service:
UTILITY_SERVICE_URL=https://your-utility-service yarn test:logger
The script uses two logger instances and batches of 100. It does not start a server or verify receipt; check utility-service for the received logs.