@blackcherie/nestjs-shared-packages
v0.1.7
Published
Shared NestJS services, guards, decorators, DTOs, and utilities with consumer-driven configuration
Maintainers
Readme
@blackcherie/nestjs-shared-packages
Shared NestJS services, guards, decorators, DTOs, and utilities for BCM projects.
Configuration follows the same pattern as @nestjs/jwt or TypeORM: you pass required data when registering a module in your app. This package does not load .env files.
Requirements
- Node.js 18+
- NestJS 11+
reflect-metadataandrxjs(standard Nest app dependencies)
Install
npm install @blackcherie/nestjs-shared-packagesPeer dependencies (install in your app if missing):
npm install @nestjs/common @nestjs/core @nestjs/config @nestjs/jwt expressFor Prisma (PostgreSQL driver adapter):
npm install @prisma/client @prisma/adapter-pgFor TCP microservice calls (TcpClientService, tcpSend, tcpEmit):
npm install @nestjs/microservicesYour app must run prisma generate so PrismaClient exists for your schema.
Quick start
1. Register modules once in AppModule
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import {
EmailModule,
RedisModule,
SqsModule,
BcryptModule,
PrismaModule,
} from '@blackcherie/nestjs-shared-packages';
import { PrismaClient } from './generated/prisma/client';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
EmailModule.forRootAsync({
isGlobal: true,
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
senderEmail: config.getOrThrow<string>('SENDER_EMAIL'),
senderPassword: config.getOrThrow<string>('SENDER_EMAIL_APP_PASSWORD'),
}),
}),
RedisModule.forRootAsync({
isGlobal: true,
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
url: config.getOrThrow<string>('REDIS_URL'),
}),
}),
SqsModule.forRootAsync({
isGlobal: true,
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
client: {
region: config.getOrThrow<string>('AWS_REGION'),
maxAttempts: 3,
retryMode: 'adaptive',
},
queueUrls: {
emailNotifications: config.getOrThrow<string>('EMAIL_NOTIFICATIONS_QUEUE_URL'),
},
}),
}),
BcryptModule.forRoot(true),
PrismaModule.forRootAsync({
isGlobal: true,
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
databaseUrl: config.getOrThrow<string>('DATABASE_URL'),
Client: PrismaClient,
}),
}),
],
})
export class AppModule {}See .env.example for variable names.
Prisma usage in services
Inject PrismaService and use .client for model access (the client class comes from your app):
import { Injectable } from '@nestjs/common';
import { PrismaService } from '@blackcherie/nestjs-shared-packages';
import { PrismaClient } from './generated/prisma/client';
@Injectable()
export class UserService {
constructor(private readonly prisma: PrismaService<PrismaClient>) {}
findAll() {
return this.prisma.client.user.findMany();
}
}2. Inject services anywhere
With isGlobal: true, feature modules do not need to import service modules again:
import { Injectable } from '@nestjs/common';
import { EmailService, RedisService, SQSService, BcryptService } from '@blackcherie/nestjs-shared-packages';
@Injectable()
export class UserService {
constructor(
private readonly email: EmailService,
private readonly redis: RedisService,
private readonly sqs: SQSService,
private readonly bcrypt: BcryptService,
) {}
async register(email: string, password: string) {
const hash = await this.bcrypt.hashPassword(password);
await this.redis.setJson(`user:${email}`, { hash }, 3600);
await this.email.sendMail(email, 'Welcome', '<p>Hello</p>');
}
}Alternative: single module (sync config)
import { SharedPackagesModule } from '@blackcherie/nestjs-shared-packages';
SharedPackagesModule.forRoot({
email: { senderEmail: '[email protected]', senderPassword: 'secret' },
redis: { url: 'redis://localhost:6379' },
sqs: {
client: { region: 'us-east-1' },
queueUrls: { emailNotifications: 'https://sqs...' },
},
}, true);For env-based config, use each module's forRootAsync (see docs/CONFIGURATION.md).
Package contents
Services
| Export | Description |
|--------|-------------|
| EmailModule / EmailService | Gmail via Nodemailer |
| RedisModule / RedisService | Redis cache (get, set, getJson, setJson, delByPrefix) |
| SqsModule / SQSService | AWS SQS with batching and deduplication |
| BcryptModule / BcryptService | Password hash and compare (bcryptjs) |
| PrismaModule / PrismaService | Prisma + PostgreSQL adapter; consumer passes databaseUrl and generated PrismaClient |
| TcpClientService / tcpSend / tcpEmit | TCP microservice client with timeout, retry, and error mapping |
| TcpClientProxy | Type for host ClientProxy (avoids duplicate @nestjs/microservices types) |
| SharedPackagesModule | Bundle multiple services (sync forRoot) |
Guards & decorators
| Export | Description |
|--------|-------------|
| AuthGuard | JWT Bearer auth (requires JwtModule in your app) |
| createAuthGuardProvider | Registers AuthGuard with your app's JwtService / Reflector (recommended for npm installs) |
| RolesGuard | Role-based access |
| @Public() | Skip AuthGuard |
| @Roles('admin', ...) | Required roles for RolesGuard |
| @CurrentUser() | Access JWT payload on request |
Validation & HTTP
| Export | Description |
|--------|-------------|
| ZodValidationPipe | Validate request body with Zod |
| GlobalExceptionFilter | Consistent error JSON responses |
| ResponseDto / request DTOs | Zod schemas for API responses |
Utilities
| Export | Description |
|--------|-------------|
| generateFilters | Build filter objects for queries |
| generateSort | Build sort objects |
| generatePassword | Random password generator |
| CustomException | Custom HTTP exception helper |
| ApiResponse | TypeScript response interface |
| logger, formatDate, isValidEmail | Small helpers |
Auth setup (consumer app)
AuthGuard uses JwtService and process.env.JWT_SECRET. Register JWT in your app.
Recommended (when installed from npm — avoids JwtService DI resolution errors):
import { Module } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtModule, JwtService } from '@nestjs/jwt';
import {
createAuthGuardProvider,
RolesGuard,
} from '@blackcherie/nestjs-shared-packages';
@Module({
imports: [
JwtModule.register({
global: true,
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '1d' },
}),
],
providers: [
createAuthGuardProvider(JwtService, Reflector),
RolesGuard,
],
})
export class AppModule {}@Public()
@Post('login')
login() { /* ... */ }
@Roles('admin')
@Get('admin')
adminOnly() { /* ... */ }TCP microservice client
Register TcpClientService in the module that calls other services over TCP:
import { Module } from '@nestjs/common';
import { TcpClientService } from '@blackcherie/nestjs-shared-packages';
@Module({
providers: [TcpClientService, AppService],
})
export class AppModule {}Call another service (pattern must match the target @MessagePattern):
import { Injectable } from '@nestjs/common';
import { ClientProxyFactory, Transport } from '@nestjs/microservices';
import { TcpClientService } from '@blackcherie/nestjs-shared-packages';
@Injectable()
export class AppService {
constructor(private readonly tcpClient: TcpClientService) {}
async getUserById(userId: string) {
const client = ClientProxyFactory.create({
transport: Transport.TCP,
options: { host: '127.0.0.1', port: 3001 },
});
try {
return await this.tcpClient.send(
client,
{ cmd: 'get_user_by_id' },
{ id: userId },
{ timeoutMs: 3000, retries: 1, retryDelayMs: 500 },
);
} finally {
client.close();
}
}
}Use matching object patterns on both sides, e.g. { cmd: 'get_user_by_id' } in the client and @MessagePattern({ cmd: 'get_user_by_id' }) on the handler.
Documentation
| Doc | Description | |-----|-------------| | docs/CONFIGURATION.md | Options for each module | | docs/PUBLISHING.md | Build and publish to npm | | CHANGELOG.md | Version history |
Development
git clone <repo>
cd nestjs-shared-packages
npm install
npm run build
npm testPublish
npm login
npm run build
npm publish --access publicSee docs/PUBLISHING.md.
