wallet-be-framework
v0.0.6
Published
Wallet backend framework for building scalable microservices with NestJS.
Downloads
4
Readme
Wallet Backend Framework
A modern, modular backend framework built on NestJS for scalable microservices architecture. This framework provides standardized modules for common backend functionality including messaging, caching, and database operations.
Introduction
The Wallet Backend Framework is designed to accelerate development of microservices by providing pre-built, configurable modules for common backend requirements. The framework includes several key modules:
- Messaging: Kafka for scalable and resilient message processing
- Caching: Dragonfly for high-performance caching with improved memory efficiency
- Analytics: ClickHouse for high-performance data analytics and time-series storage
- Audit: Comprehensive audit logging capabilities
- Indexer: Efficient data indexing services
- Bucket: Object storage integration
- Logging: Structured logging with context tracking
Each module follows a consistent pattern with async configuration options, making them easy to integrate into any NestJS application.
Installation
npm install wallet-be-frameworkOr using yarn:
yarn add wallet-be-frameworkGetting Started
To use the Wallet Backend Framework in your NestJS application, import the modules you need and configure them in your application module:
Bucket Module
Module Registration
import { BucketModule } from 'wallet-be-framework';
@Module({
imports: [
BucketModule.forRootAsync({
imports: [AppConfigModule],
inject: [AppConfigService],
useFactory: (config: AppConfigService) => ({
provider: 'minio',
endpoint: config.bucketEndpoint,
cdnEndpoint: config.bucketCdnEndpoint,
key: config.bucketKey,
secret: config.bucketSecret,
defaultConf: {
defaultBucket: config.bucketDefaultBucket,
defaultPrefix: config.bucketDefaultPrefix,
},
env: config.env,
}),
}),
]
})
export class AppModule {}Available Methods
getSignedUrl(key: string, options: SignedUrlOptions): Promise<string>getSignedPutUrl(key: string, options: SignedUrlOptions): Promise<string>upload(key: string, content: string | Buffer, options: UploadOptions): Promise<string>uploadFile(file: Buffer, key: string, options?: UploadOptions): Promise<string>getObject(key: string): Promise<Buffer>delete(key: string, options: SignedUrlOptions): Promise<void>deleteObject(key: string): Promise<void>
Kafka Module
The Kafka module provides a robust messaging system for your microservices architecture, replacing traditional RabbitMQ with Kafka's more scalable approach.
Module Registration
import { KafkaModule } from 'wallet-be-framework';
@Module({
imports: [
KafkaModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
clientId: configService.get('KAFKA_CLIENT_ID'),
brokers: configService.get('KAFKA_BROKERS').split(','),
ssl: configService.get('KAFKA_SSL') === 'true',
sasl: configService.get('KAFKA_SASL') === 'true' ? {
mechanism: 'plain',
username: configService.get('KAFKA_USERNAME'),
password: configService.get('KAFKA_PASSWORD'),
} : undefined,
}),
}),
],
})
export class AppModule {}Using the KafkaService
import { Injectable } from '@nestjs/common';
import { KafkaService } from 'wallet-be-framework';
@Injectable()
export class YourService {
constructor(private readonly kafkaService: KafkaService) {}
async sendMessage(topic: string, message: any): Promise<void> {
await this.kafkaService.produce(topic, message);
}
async setupConsumer(topic: string, groupId: string): Promise<void> {
await this.kafkaService.consume(
topic,
groupId,
async (message) => {
// Process the message
console.log('Received message:', message.value.toString());
},
);
}
}Dragonfly Cache Module
The Dragonfly Cache module provides high-performance caching capabilities, replacing traditional Redis with the more memory-efficient Dragonfly.
Module Registration
import { DragonflyCacheModule } from 'wallet-be-framework';
@Module({
imports: [
DragonflyCacheModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
url: configService.get('DRAGONFLY_URL'),
ttl: parseInt(configService.get('DRAGONFLY_TTL') || '3600', 10),
}),
}),
],
})
export class AppModule {}Using the DragonflyCacheService
import { Injectable } from '@nestjs/common';
import { DragonflyCacheService } from 'wallet-be-framework';
@Injectable()
export class YourService {
constructor(private readonly cacheService: DragonflyCacheService) {}
async getData(key: string): Promise<any> {
// Try to get data from cache first
const cachedData = await this.cacheService.get(key);
if (cachedData) {
return cachedData;
}
// If not in cache, get from source and cache it
const data = await this.fetchDataFromSource();
await this.cacheService.set(key, data, 3600); // Cache for 1 hour
return data;
}
private async fetchDataFromSource(): Promise<any> {
// Your implementation to fetch data from the source
return { example: 'data' };
}
}Using the Caching Decorator
import { Injectable } from '@nestjs/common';
import { Cacheable } from 'wallet-be-framework';
@Injectable()
export class YourService {
@Cacheable('your-cache-key', 3600) // Cache for 1 hour
async getExpensiveData(id: string): Promise<any> {
// This function will be cached automatically
// It will only execute if the cache doesn't have the result
return this.fetchExpensiveDataFromSource(id);
}
private async fetchExpensiveDataFromSource(id: string): Promise<any> {
// Your implementation
return { id, data: 'expensive data' };
}
}ClickHouse Module
The ClickHouse module provides high-performance analytics and time-series data storage capabilities.
Module Registration
import { ClickHouseModule } from 'wallet-be-framework';
@Module({
imports: [
ClickHouseModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
host: configService.get('CLICKHOUSE_HOST'),
port: parseInt(configService.get('CLICKHOUSE_PORT') || '8123', 10),
username: configService.get('CLICKHOUSE_USERNAME'),
password: configService.get('CLICKHOUSE_PASSWORD'),
database: configService.get('CLICKHOUSE_DATABASE'),
}),
}),
],
})
export class AppModule {}Using the ClickHouseService
import { Injectable } from '@nestjs/common';
import { ClickHouseService } from 'wallet-be-framework';
@Injectable()
export class AnalyticsService {
constructor(private readonly clickhouseService: ClickHouseService) {}
async logEvent(event: any): Promise<void> {
await this.clickhouseService.insert('events', [event]);
}
async getAnalytics(startDate: Date, endDate: Date): Promise<any[]> {
const query = `
SELECT
event_type,
count() as count
FROM events
WHERE event_date BETWEEN {startDate: Date} AND {endDate: Date}
GROUP BY event_type
`;
return this.clickhouseService.query(query, {
startDate,
endDate,
});
}
}Audit Module
The Audit module provides comprehensive audit logging capabilities for tracking changes and actions within your application.
Module Registration
import { AuditModule } from 'wallet-be-framework';
@Module({
imports: [
AuditModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
serviceName: configService.get('SERVICE_NAME'),
storage: {
type: 'clickhouse',
options: {
host: configService.get('CLICKHOUSE_HOST'),
port: parseInt(configService.get('CLICKHOUSE_PORT') || '8123', 10),
username: configService.get('CLICKHOUSE_USERNAME'),
password: configService.get('CLICKHOUSE_PASSWORD'),
database: configService.get('CLICKHOUSE_DATABASE'),
table: 'audit_logs',
},
},
}),
}),
],
})
export class AppModule {}Using the AuditService
import { Injectable } from '@nestjs/common';
import { AuditService } from 'wallet-be-framework';
@Injectable()
export class UserService {
constructor(private readonly auditService: AuditService) {}
async updateUser(userId: string, data: any, requestContext: any): Promise<void> {
// Get current state
const currentUser = await this.getUserById(userId);
// Perform update
const updatedUser = await this.performUpdate(userId, data);
// Log the audit event
await this.auditService.log({
action: 'UPDATE_USER',
entityType: 'USER',
entityId: userId,
oldValue: currentUser,
newValue: updatedUser,
metadata: {
requestId: requestContext.requestId,
userId: requestContext.userId,
ipAddress: requestContext.ipAddress,
},
});
return updatedUser;
}
private async getUserById(userId: string): Promise<any> {
// Implementation
}
private async performUpdate(userId: string, data: any): Promise<any> {
// Implementation
}
}Additional Modules
The framework also includes several other modules:
- Indexer Module: For efficient data indexing services
- Logging Module: For structured logging with context tracking
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
getSignedPutUrl(key: string, options: SignedUrlOptions): Promise<string>upload(key: string, content: string | Buffer, options: UploadOptions): Promise<string>delete(key: string, options: SignedUrlOptions): Promise<string>buildUrl(key: string): string
Dragonfly Cache Module
Module Registration
import { DragonflyModule } from '@mosu/be-framework';
@Module({
imports: [
DragonflyModule.forRootAsync({
imports: [AppConfigModule],
inject: [AppConfigService],
useFactory: (config: AppConfigService) => ({
url: config.redisUrl, // Works with both Redis and Dragonfly servers
ttl: 5 * 60 * 1000, // 5 minutes
maxReconnectAttempts: 5,
reconnectDelay: 5000, // 5 seconds
pingInterval: 5000, // 5 seconds
crashOnConnectFailure: true,
}),
}),
],
})
export class AppModule {}Available Methods
get<T>(key: string): Promise<T | undefined>set<T>(key: string, value: T, ttl?: number): Promise<void>del(key: string): Promise<void>reset(): Promise<void>wrap<T>(key: string, fn: () => Promise<T>, ttl?: number): Promise<T>mset<T>(keyValuePairs: Array<[string, T]>, ttl?: number): Promise<void>mget<T>(keys: string[]): Promise<(T | null)[]>exists(key: string): Promise<boolean>
Location Module
Module Registration
import { LocationModule } from '@mosu/be-framework';
@Module({
imports: [
LocationModule.forRootAsync({
imports: [AppConfigModule],
inject: [AppConfigService],
useFactory: (config: AppConfigService) => ({
apiKey: config.apiKey,
}),
}),
]
})
export class AppModule {}Available Methods
validateAddress(address: string): Promise<AddressValidationOutput>autocompleteAddress(payload: AddressAutocompleteInput): Promise<AddressValidationOutput[]>
Kafka Module
Module Registration
import { KafkaModule } from '@mosu/be-framework';
@Module({
imports: [
KafkaModule.forRootAsync({
imports: [AppConfigModule],
inject: [AppConfigService],
useFactory: (config: AppConfigService) => ({
clientId: 'my-app',
brokers: config.kafkaBrokers,
ssl: config.kafkaUseSsl,
topics: [
{ name: 'user-events', numPartitions: 3, replicationFactor: 2 },
],
consumerGroups: [
{ groupId: 'user-events-group', topics: ['user-events'] },
],
}),
}),
]
})
export class AppModule {}Available Methods
createProducer(config?: ProducerConfig): Promise<void>createTopic(topicConfig: KafkaTopicConfig): Promise<void>publish<T>(topic: string, message: T, key?: string): Promise<void>consume(groupId: string, topics: string[], config?: ConsumerConfig): Promise<ConsumerSubject>subscribeConsumer(consumerGroup: string, topic: string, messageKey?: string): Promise<ConsumerSubject>checkHealth(): Promise<KafkaHealthStatus>
ClickHouse Module
Module Registration
import { ClickHouseModule } from '@mosu/be-framework';
@Module({
imports: [
ClickHouseModule.forRootAsync({
imports: [AppConfigModule],
inject: [AppConfigService],
useFactory: (config: AppConfigService) => ({
host: config.clickhouseHost,
port: config.clickhousePort,
username: config.clickhouseUsername,
password: config.clickhousePassword,
database: config.clickhouseDatabase,
}),
}),
]
})
export class AppModule {}Available Methods
query<T>(sql: string, params?: Record<string, any>): Promise<T[]>insert<T>(table: string, data: T | T[]): Promise<void>createTable(tableName: string, schema: Record<string, string>): Promise<void>checkHealth(): Promise<{ status: 'ok' | 'error', details?: string }>
