caserita-infra
v0.13.4
Published
Library infra caserita
Readme
🗺️ Índice
- Caserita Infra
- 📄 Requirements
- 🚀 Instalación
- 🌎 Publicación
- 📖 Project Structure
- Usage and Examples
- 📝 Environment Variables
- 🛠️ Scripts Disponibles
- 🚀 Deployment
- 😁 Authors
Caserita Infra
NestJS is a utility and package library designed to simplify and standardize the development of microservices with a hexagonal or n-tier architecture, using NestJS and TypeScript. Package management is handled with pnpm, ensuring rapid installation and efficient use of disk space.
📄 Requirements
- Node.js >= 22.14.0
- pnpm >= 10.x
🚀 Installation
# Install dependencies
pnpm install
# Development
pnpm run start:dev
# Production
pnpm run start:prod
# Build
pnpm run build🌎 Publication
# Increase a version path (1.0.0) -> (1.0.1)
$ pnpm version patch
# Increase a minor version (1.0.0) -> (1.1.0)
$ pnpm version minor
# Increase a major version (1.0.0) -> (2.0.0)
$ pnpm version major
# Increase a specific version (1.0.0) -> (3.1.1)
$ pnpm version <version>
# Publish the package
$ pnpm publish📖 Project Structure
The repository is organized into two main directories:
libs/: Contains reusable modules and packages for various functionalities.src/: Includes the source code for how to use these libraries and how to test them.
[libs]/
├── [adapter]/ # [Adapters for external transport services, such as NATS, Kafka, SMTP, for microservices.]
├── [cdecorator]/ # [Provides custom decorators for NestJS.]
├── [common-cache]/ # [Functions for cache management, such as REDIS or local.]
├── [common-database]/ # [This module facilitates interaction with the database]
├── [common-encrypted]/ # [Utilities for data encryption and decryption.]
├── [common-http]/ # [Centralizes utilities for handling HTTP requests and responses]
├── [common-logger]/ # [Provides a unified logging system, crucial for application monitoring and debugging.]
├── [common-response]/ # [Standardized response structures for APIs, Events, such as success and error.]
├── [common-search]/ # [Advanced search logic, with respect to databases.]
└── index.ts # [Main entry point]Usage/Examples
adapter
Adapters for external transport services, such as NATS, Kafka, SMTP, for microservices.
Use:
The way to inject the module:
natsTransport.module.ts
import { Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { AdapterModule, Adapter } from 'caserita-infra/packages/adapter';
import { NATS_MS_SERVICE_INJECT } from '@app/utils/constants.util'
@Module({
imports: [
AdapterModule,
ClientsModule.registerAsync([
{
name: NATS_MS_SERVICE_INJECT,
useFactory: (adapter: Adapter) => {
const natsConfig = adapter.config();
return natsConfig;
},
inject: [Adapter],
},
]),
],
exports: [ClientsModule],
})
export class NatsTransportModule {}
How to use the module on the client:
caseritaTaxidentityvalidationMsEvent.client.ts
import { Inject, Injectable } from "@nestjs/common";
import { ConfigService } from '@nestjs/config';
import { ClientProxy } from '@nestjs/microservices';
import { firstValueFrom } from 'rxjs';
import { NATS_MS_SERVICE_INJECT } from '@app/utils/constants.util';
import { CaseritaTaxidentityvalidationMsService } from "@domain/caseritaTaxidentityvalidationMs.service";
import { CaseritaTaxidentityvalidationMsFindCompanyByRucRequest } from "@domain/interfaces/caseritaTaxidentityvalidationMsFindCompanyByRucRequest.interface";
import { CaseritaTaxidentityvalidationMsFindCompanyByRucResponse } from "@domain/interfaces/caseritaTaxidentityvalidationMsFindCompanyByRucResponse.interface";
@Injectable()
export class CaseritaTaxidentityvalidationMsEventClient implements CaseritaTaxidentityvalidationMsService {
private caseritaTaxIdentityValidationMsFindCompayByRuc: string;
private caseritaTaxIdentityValidationMsFindLegalRepresentativeByRuc: string;
private caseritaTaxIdentityValidationMsFindPersonByDni: string;
constructor(
private readonly configService: ConfigService,
@Inject(NATS_MS_SERVICE_INJECT) private readonly client: ClientProxy
) {
this.caseritaTaxIdentityValidationMsFindCompayByRuc = this.configService.get<string>('CASERITA_TAXIDENTITYVALIDATION_MS_FIND_COMPANY_BY_RUC');
this.caseritaTaxIdentityValidationMsFindLegalRepresentativeByRuc = this.configService.get<string>('CASERITA_TAXIDENTITYVALIDATION_MS_FIND_LEGAL_REPRESENTATIVE_BY_RUC');
this.caseritaTaxIdentityValidationMsFindPersonByDni = this.configService.get<string>('CASERITA_TAXIDENTITYVALIDATION_MS_FIND_PERSON_BY_DNI');
}
findCompanyByRuc(input: CaseritaTaxidentityvalidationMsFindCompanyByRucRequest): Promise<CaseritaTaxidentityvalidationMsFindCompanyByRucResponse> {
const { ruc } = input;
return firstValueFrom(this.client.send({ cmd: this.caseritaTaxIdentityValidationMsFindCompayByRuc }, { ruc }));
}
}
Environments:
This package uses the following variables:
.env .env.development.local
NATS_SERVERS="nats://localhost:4222"
cdecorator
Provides custom decorators for NestJS.
Use:
How to use the module on the controller:
store.controller.ts
import { Controller, Post } from '@nestjs/common';
import { CommandBus, QueryBus } from '@nestjs/cqrs';
import { ValidateBody } from 'caserita-infra/packages/cdecorator';
import { CreateCommandImpl } from '@app/commands/impl/create-command.impl';
import { CreateInputValidator } from '@infra/validators/create.input.validator';
import { CreateStoreRequest } from '@domain/interfaces/createStoreRequest.interface';
@Controller('stores')
export class StoreController {
constructor(
private readonly commandBus: CommandBus,
private readonly queryBus: QueryBus,
) {}
@Post('/create')
create(@ValidateBody(CreateInputValidator) input: CreateStoreRequest) {
return this.commandBus.execute(new CreateCommandImpl(input));
}
}
common-cache
Functions for cache management, such as REDIS or local.
Use:
The way to inject the module:
app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { CommonCacheModule } from '@caserita/common-cache';
import { AppController } from './app.controller';
import { AppHandler } from './app.handler';
import { AppService } from './app.service';
import { AppRepository } from './app.repository';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, cache: true }),
CommonCacheModule,
],
controllers: [AppController, AppHandler],
providers: [AppService, AppRepository],
})
export class AppModule {}
How to use the module on the service:
app.service.ts
import { ConfigService } from '@nestjs/config';
import { Injectable } from '@nestjs/common';
import { RedisCacheService } from '@caserita/common-cache';
import { AppRepository } from './app.repository';
@Injectable()
export class AppService {
private baseUrl: string;
constructor(
private readonly configService: ConfigService,
private readonly cacheService: RedisCacheService,
private readonly appRepository: AppRepository,
) {
this.baseUrl = this.configService.get<string>('BASE_URL_DATTEBAYO');
}
async getRedisCloud() {
return this.cacheService.get('HOLA:1236');
}
async setRedisCloud() {
await this.cacheService.set('HOLA:1239', { A: '1234', B: 'xyz' }, 300); //ttl 300 equivale a 5 minutos
}
}
Environments:
This package uses the following variables:
.env .env.development.local
REDIS_CLOUD_HOST=example.redis-cloud.com
REDIS_CLOUD_PORT=18472
REDIS_CLOUD_PASSWORD=vHqWgOtqqZXoA18km0Y76XiI4iNnr8DZ
REDIS_CLOUD_TTL= 300
REDIS_NUMBER_DB=
common-database
This module facilitates interaction with the database
Use:
The way to inject the module:
app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import {
CommonDatabaseModule,
DatabaseConnectionType,
DatabaseEnumType,
} from '@caserita/common-database';
// import { CommonCacheModule } from '@caserita/common-cache';
import { CommonEncryptedModule, EncryptedProviderType, ValueTransformer, HashTransformer } from '@caserita/common-encrypted';
import { AppController } from './app.controller';
import { AppHandler } from './app.handler';
import { EntityModule } from './entity/entity.module';
import { AppService } from './app.service';
import { AppRepository } from './app.repository';
import { AppEntity } from './entity/app.entity';
import { AppPostgresEntity } from './entity/appPostgres.entity';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, cache: true }),
CommonDatabaseModule.register([
{
name: DatabaseConnectionType.MONGODB_CONNECTION,
type: DatabaseEnumType.MONGODB,
entities: [AppEntity],
},
{
name: DatabaseConnectionType.POSTGRES_CONNECTION,
type: DatabaseEnumType.POSTGES,
entities: [AppPostgresEntity],
// If you need to encrypt data in SQL
imports: [CommonEncryptedModule.register(EncryptedProviderType.DATABASE)],
inject: [ValueTransformer, HashTransformer],
columnsTransformers: ['fieldencrypt', 'fieldencrypt_index', 'fieldencrypttwo'],
},
]),
EntityModule,
],
controllers: [AppController, AppHandler],
providers: [AppService, AppRepository],
})
export class AppModule {}
How to use the module on the entity:
entity.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DatabaseConnectionType } from '@caserita/common-database';
import { AppEntity } from './app.entity';
import { AppPostgresEntity } from './appPostgres.entity';
// The "forFeature" already creates the providers (the repositories)
const mongoRepositoryModule = TypeOrmModule.forFeature([AppEntity], DatabaseConnectionType.MONGODB_CONNECTION);
const postgresRepositoryModule = TypeOrmModule.forFeature([AppPostgresEntity], DatabaseConnectionType.POSTGRES_CONNECTION);
@Module({
imports: [mongoRepositoryModule, postgresRepositoryModule],
// This is where the change lies: you must export the entity classes that act as
// tokens for the repositories. This causes TypeOrmModule to expose the repositories
// created in the "imports" process.
exports: [mongoRepositoryModule, postgresRepositoryModule],
})
export class EntityModule {}
Environments:
This package uses the following variables:
.env .env.development.local
# MONGO DATABASE
DB_USER_MONGO=example
DB_KEY_MONGO=7247uriu2482rw247
DB_HOST_MONGO=example.mongodb.net
DB_CONTAINER_NAME_MONGO=cstainfradbdev
DB_SYNCHRONIZE_MONGO=true
# POSTGRES DATABASE
DB_HOST_POSTGRES=example.supabase.com
DB_PORT_POSTGRES=6543
DB_USERNAME_POSTGRES=postgres.qsyvlxmwyodpyevujbgx
DB_PASSWORD_POSTGRES=7247uriu2482rw247
DB_CONTAINER_NAME_POSTGRES=cstainfradbdev
DB_SYNCHRONIZE_POSTGRES=true
# ENCRYPTION DATBASE SQL
ENCRYPT_DATABASE_SECRET_KEY=xDkjHdp132nRLBjUOcvHGAJzb7dGmt
common-encrypted
Utilities for data encryption and decryption.
Use:
The way to inject the module:
app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { CommonEncryptedModule, EncryptedProviderType } from '@caserita/common-encrypted';
import { AppController } from './app.controller';
import { AppHandler } from './app.handler';
import { AppService } from './app.service';
import { AppRepository } from './app.repository';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, cache: true }),
CommonEncryptedModule.register(EncryptedProviderType.APP),
],
controllers: [AppController, AppHandler],
providers: [AppService, AppRepository],
})
export class AppModule {}
How to use the module on the service:
app.service.ts
import { ConfigService } from '@nestjs/config';
import { Injectable } from '@nestjs/common';
import { CipherEngine } from '@caserita/common-encrypted';
import { AppRepository } from './app.repository';
@Injectable()
export class AppService {
private baseUrl: string;
constructor(
private readonly configService: ConfigService,
private readonly cipherEngine: CipherEngine,
private readonly appRepository: AppRepository,
) {
this.baseUrl = this.configService.get<string>('BASE_URL_DATTEBAYO');
}
async encryption(entry: string) {
return this.cipherEngine.encrypt(entry);
}
async decryptyion(entry: string) {
return this.cipherEngine.decrypt(entry);
}
}
Environments:
This package uses the following variables:
.env .env.development.local
ENCRYPT_SECRET_KEY=WwMIK54FypnmSR1JPSCdcwxcwCl2cA
ENCRYPT_DATABASE_SECRET_KEY=xDkjHdp132nRLBjUOcvHGAJzb7dGmt
common-http
Centralizes utilities for handling HTTP requests and responses
Use:
The way to inject the module:
app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { CommonHttpModule } from '@caserita/common-http';
import { AppController } from './app.controller';
import { AppHandler } from './app.handler';
import { AppService } from './app.service';
import { AppRepository } from './app.repository';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, cache: true }),
CommonHttpModule,
],
controllers: [AppController, AppHandler],
providers: [AppService, AppRepository],
})
export class AppModule {}
How to use the module on the client:
app.service.ts
import { ConfigService } from '@nestjs/config';
import { HttpException, Injectable } from '@nestjs/common';
import { catchError, firstValueFrom } from 'rxjs';
import { AxiosError } from 'axios';
import { HttpClient } from '@caserita/common-http';
@Injectable()
export class AppService {
private baseUrl: string;
constructor(
private readonly httpClient: HttpClient,
private readonly configService: ConfigService,
) {
this.baseUrl = this.configService.get<string>('BASE_URL_DATTEBAYO');
}
async proxy() {
return firstValueFrom(
this.httpClient.get(`${this.baseUrl}/characters/1344`).pipe(
catchError((error: AxiosError) => {
throw new HttpException(
{
errorCode: error.code,
message: error.response.data,
stack: error.stack,
},
error.status,
);
}),
),
);
}
}
common-logger
Provides a unified logging system, crucial for application monitoring and debugging.
Use:
The way to inject the module:
app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { CommonLoggerModule } from '@caserita/common-logger';
import { AppController } from './app.controller';
import { AppHandler } from './app.handler';
import { AppService } from './app.service';
import { AppRepository } from './app.repository';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, cache: true }),
CommonLoggerModule,
],
controllers: [AppController, AppHandler],
providers: [AppService, AppRepository],
})
export class AppModule {}
How to use the module on the use-case:
create-command.handler.ts
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { Logger } from '@nestjs/common';
import { CreateCommandImpl } from '@app/commands/impl/create-command.impl';
import { ExpedientService } from '@domain/expedient.service';
import { CaseritaBusinessAccountMsService } from '@domain/caseritaBusinessAccountMs.service';
import { Expedient } from '@domain/entities/expedient.entity';
import { ExpedientTracing } from '@domain/entities/expedientTracing.entity';
import { DatosEmpresa } from '@domain/entities/datosEmpresa';
import { DatosRepresentanteLegal } from '@domain/entities/datosRepresentanteLegal';
import { Archivo } from '@domain/entities/archivo.entity';
import { EstadoExpediente } from '@domain/emun/estadoExpediente.enum';
import { TipoDocumento } from '@domain/emun/tipoDocumento.enum';
@CommandHandler(CreateCommandImpl)
export class CreateCommandHandler implements ICommandHandler<CreateCommandImpl> {
private readonly logger = new Logger('CreateCommandHandler');
constructor(
private readonly service: ExpedientService,
private readonly caseritaBusinessAccountMs: CaseritaBusinessAccountMsService,
) {}
async execute({ command }: CreateCommandImpl): Promise<void> {
const expedient = new Expedient();
const expedientTracing = new ExpedientTracing();
const datosEmpresa = new DatosEmpresa();
const datosRepresentanteLegal = new DatosRepresentanteLegal();
const archivo = new Archivo();
const archivos: Archivo[] = [];
expedient.estado = EstadoExpediente.CREADO;
expedient.codigoExpediente = this.generateCodeExpediente(
command.datosEmpresa.numeroDocumento,
command.datosRepresentanteLegal.numeroDocumento,
);
datosEmpresa.tipoDocumento = TipoDocumento.RUC;
datosEmpresa.numeroDocumento = command.datosEmpresa.numeroDocumento;
datosRepresentanteLegal.tipoDocumento = TipoDocumento.DNI;
datosRepresentanteLegal.numeroDocumento = command.datosRepresentanteLegal.numeroDocumento;
datosRepresentanteLegal.fechaNacimiento = command.datosRepresentanteLegal.fechaNacimiento;
datosRepresentanteLegal.correoElectronico = command.datosRepresentanteLegal.correoElectronico;
datosRepresentanteLegal.direccion = command.datosRepresentanteLegal.direccion;
datosRepresentanteLegal.telefono = command.datosRepresentanteLegal.telefono;
for (const a of command.archivos) {
archivo.nombreArchivo = a.nombreArchivo;
archivo.url = a.url;
archivo.tipoArchivo = a.tipoArchivo;
archivo.fechaSubida = a.fechaSubida;
archivos.push(archivo);
}
expedient.datosEmpresa = datosEmpresa;
expedient.datosRepresentanteLegal = datosRepresentanteLegal;
expedient.archivos = archivos;
const result = await this.service.create(expedient);
this.logger.log('[CreateCommandImpl - CREATE EXPEDIENTE]: Se creo el expediente con éxito');
// Envio mensaje service bus para proceso asincrono de creacion de cuenta negocio
this.logger.log('[CreateCommandImpl - SEND CREATE ASYNC BUSINESS ACCOUNT]');
this.caseritaBusinessAccountMs.create({ idExpediente: expedient.idExpediente, idEmpresa: expedient.codigoExpediente });
expedientTracing.codigoExpediente = result.codigoExpediente;
expedientTracing.estado = EstadoExpediente.CREADO;
expedientTracing.ruc = datosEmpresa.numeroDocumento;
expedientTracing.dni = datosRepresentanteLegal.numeroDocumento;
expedientTracing.fechaCreacion = result.fechaCreacion.toISOString();
await this.service.createTracing(expedientTracing);
this.logger.log(
'[CreateCommandImpl - CREATE EXPEDIENTE TRACING]: Se creo el trazado del expediente con éxito',
);
}
private generateCodeExpediente(rucNumber: string, dniNumber: string) {
return `${TipoDocumento.RUC}${rucNumber}${TipoDocumento.DNI}${dniNumber}`;
}
}
Environments:
This package uses the following variables:
.env .env.development.local
LOGGER_LEVEL=info
LOGGER_ENABLED=true # false
LOGGER_CLOUD=logtail # local -> TOKEN_LOGTAIL_LOGGER is empty
TOKEN_LOGTAIL_LOGGER=82jk4y28y283yru2734728
common-response
Standardized response structures for APIs, Events, such as success and error.
common-response
Advanced search logic, with respect to databases.
📝 Environment Variables
To run this project, you will need to add the following environment variables to your .env.development.local file
.env.development.local
# APP
MICROSERVICE_NAME=INFRA
APP_RUNNING_ENV=HTTP # NATS
NODE_ENV=development # production
PORT=8080
# NATS
NATS_SERVERS="nats://localhost:4222"
# LOGGER
LOGGER_LEVEL=info
LOGGER_ENABLED=true
LOGGER_CLOUD=logtail
TOKEN_LOGTAIL_LOGGER=******
# DATABASE
DB_USER_MONGO=******
DB_KEY_MONGO=******
DB_HOST_MONGO=******
DB_CONTAINER_NAME_MONGO=******
# POSTGRES DATABASE
DB_HOST_POSTGRES=******
DB_PORT_POSTGRES=******
DB_USERNAME_POSTGRES=******
DB_PASSWORD_POSTGRES=******
DB_CONTAINER_NAME_POSTGRES=******
DB_SYNCHRONIZE_POSTGRES=******
# ENCRYPTION
ENCRYPT_SECRET_KEY=******
ENCRYPT_DATABASE_SECRET_KEY=******
# CACHE REDIS
REDIS_CLOUD_HOST=******
REDIS_CLOUD_PORT=******
REDIS_CLOUD_PASSWORD=******
REDIS_CLOUD_TTL= 300
REDIS_NUMBER_DB=
# PROXIES
BASE_URL_DATTEBAYO=https://dattebayo-api.onrender.com
🛠️ Scripts Disponibles
Here are some related projects
# Desarrollo
pnpm run start:dev # Ejecuta en modo desarrollo con watch
pnpm run start # Ejecuta en modo normal
pnpm run start:prod # Ejecuta en modo producción
# Build
pnpm run build # Compila el proyecto
# Testing
pnpm run test # Ejecuta tests unitarios
pnpm run test:watch # Ejecuta tests en modo watch
pnpm run test:e2e # Ejecuta tests end-to-end
pnpm run test:cov # Ejecuta tests con coverage
# Linting y Formato
pnpm run lint # Ejecuta ESLint
pnpm run format # Formatea código con Prettier
🚀 Deployment
To deploy this project with docker:
FROM node:18-alpine
WORKDIR /app
# Instalar pnpm
RUN npm install -g pnpm
# Copiar archivos de dependencias
COPY package.json pnpm-lock.yaml ./
# Instalar dependencias
RUN pnpm install --frozen-lockfile
# Copiar código fuente
COPY . .
# Build
RUN pnpm run build
# Exponer puerto
EXPOSE 3000
# Comando de inicio
CMD ["pnpm", "run", "start:prod"]
docker-compose:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:password@db:5432/caserita
depends_on:
- db
- redis
db:
image: postgres:15
environment:
POSTGRES_DB: caserita
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
😁 Authors
- Desarrollado con ❤️ por BrolyDev
