@jrosadob/ms-sophia-framework-mysql
v0.0.4
Published
node -v npm -v nest --version npm i -g @nestjs/cli # Revisar disponibilidad npm view ms-sophia-framework-mysql version # Crear el workspace (No funciona con pnpm) nest new ms-sophia-framework-mysql --package-manager npm # Ingresar cd ms-sophia-framework-m
Readme
Sophia Framework
node -v npm -v nest --version npm i -g @nestjs/cli
Revisar disponibilidad
npm view ms-sophia-framework-mysql version
Crear el workspace (No funciona con pnpm)
nest new ms-sophia-framework-mysql --package-manager npm
Ingresar
cd ms-sophia-framework-mysql
Generar la libreria
nest g library ms-sophia-framework-mysql
Compilar
npm run build
Validsate
nest build ms-sophia-framework-mysql
Pack en tg.gz para despliegue local
npm pack
Publish
npm publish --access public
v47
Define propiedes dinamicas para el datasource: entities, subscribers, synchronize, logging, logger: typeOrmLogger
Publish
- Actualizar version in package.json
- Run
./publish.sh
CLI
$ pnpm install
$ pnpm add @nestjs/config # Install package
$ pnpm run start # Development
$ pnpm run start:dev # Wath mode
$ pnpm run start:prod # Production mode
$ pnpm run test # Unit tests
$ pnpm run test:e2e # e2e tests
$ pnpm run test:cov # Coverage testPackages
pnpm add @nestjs/config
pnpm add @nestjs/swagger
pnpm add @nestjs/typeorm
pnpm add axios
pnpm add class-transformer
pnpm add class-validator
pnpm add js
pnpm add js-yaml
pnpm add mysql2
pnpm add nanoid
pnpm add typeorm
pnpm add @nestjs/microservicesPreHook
app.useGlobalInterceptors(new LoggingInterceptor(configService));
app.useGlobalFilters(new AllExceptionsFilter());Idempotencia de Controladores TCP
Asegurarse de que sea idempotente Si es para traer datos no hay problema Pero si es para guardar datos, debe usarse una transaccion con una tabla que tiene PK ms-accounts, module-name y method-name. Si todo sale bien enviar el ACK. Si se detacto duplicado no ejecutar el metodo de negocio, pero enviar el ACK para que RabbitMQ no siga enviando mensajes.
@MessagePattern({ cmd: 'charge' })
async handleCharge(@Payload() data: any, @Ctx() ctx: RmqContext) {
const channel = ctx.getChannelRef();
const msg = ctx.getMessage();
const key =
msg.properties?.headers?.['x-idempotency-key'] ||
msg.properties?.messageId;
if (!key) {
// Sin clave no hay deduplicación confiable
// Decide: rechazar o procesar con riesgo
channel.nack(msg, false, false);
return;
}
try {
await this.dataSource.transaction(async (manager) => {
// 1) Intentar registrar el mensaje
// Si falla por unique, es duplicado
await manager.query(
`INSERT INTO processed_messages (consumer_name, message_key)
VALUES (?, ?)`,
['payments-consumer', key],
);
// 2) Lógica de negocio real (solo primera vez)
await this.paymentService.charge(data, manager);
});
// 3) Confirmar
channel.ack(msg);
} catch (e: any) {
// Duplicado por unique constraint: confirmar y salir
if (isDuplicateKeyError(e)) {
channel.ack(msg);
return;
}
// Error real: reintentar o enviar a DLQ según estrategia
channel.nack(msg, false, true);
}
}