@tavos/fgsrv-shared-lib
v2.1.0
Published
Shared infrastructure library for FlowGate NestJS microservices — tracing, logging, health, transactional outbox and inbox, multi-tenant context, errors and graceful shutdown
Maintainers
Readme
@tavos/fgsrv-shared-lib
Shared infrastructure library for FlowGate NestJS microservices.
Provides production-ready modules for tracing, structured logging, health checks, transactional outbox pattern, multi-tenant isolation, audit trails, feature flags, and graceful shutdown.
Installation
npm install @tavos/fgsrv-shared-libPeer Dependencies
This library uses peer dependencies to avoid version conflicts. Install the modules you need:
# Core (required)
npm install @nestjs/common @nestjs/core rxjs reflect-metadata
# Tracing (for OpenTelemetry support)
npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-grpc @opentelemetry/exporter-metrics-otlp-grpc \
@opentelemetry/resources @opentelemetry/sdk-metrics
# Logging
npm install nestjs-pino pino pino-http
# Health checks
npm install @nestjs/terminus
# Outbox & Tenant isolation
npm install @prisma/client
# Feature flags
npm install @nestjs/config
# Redis health indicator
npm install ioredisModules
Tracing (OpenTelemetry)
Bootstrap tracing before your NestJS app starts:
// main.ts — must be the FIRST import
import '@tavos/fgsrv-shared-lib/tracing';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';Use the @Span() decorator for custom business logic tracing:
import { Span } from '@tavos/fgsrv-shared-lib';
@Injectable()
export class DeviceService {
@Span('device.provision')
async provisionDevice(dto: CreateDeviceDto) {
// Automatically traced with OpenTelemetry
}
}Environment variables:
| Variable | Default | Description |
|---|---|---|
| SERVICE_NAME | unknown-service | OTel service name |
| SERVICE_VERSION | 1.0.0 | OTel service version |
| OTEL_EXPORTER_OTLP_ENDPOINT | http://localhost:4317 | OTel Collector endpoint |
| OTEL_DEBUG | false | Enable OTel debug logging |
Logging (Pino + OTel)
import { LoggingModule } from '@tavos/fgsrv-shared-lib';
@Module({
imports: [LoggingModule],
})
export class AppModule {}Features: JSON structured logs, automatic traceId/spanId injection, request correlation via X-Request-Id, sensitive field redaction, health endpoint filtering.
Health Checks
import { HealthModule } from '@tavos/fgsrv-shared-lib';
@Module({
imports: [HealthModule],
})
export class AppModule {}Exposes GET /health/live (liveness) and GET /health/ready (readiness) endpoints for Kubernetes probes.
Custom health indicators included: RedisHealthIndicator, RabbitMQHealthIndicator.
Multi-Tenant Isolation
import {
TenantMiddleware,
TenantGuard,
TenantContext,
createPrismaTenantMiddleware,
} from '@tavos/fgsrv-shared-lib';
// In AppModule
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(TenantMiddleware).forRoutes('*');
}
}
// In service — automatic tenant scoping for Prisma
prisma.$use(createPrismaTenantMiddleware(() => tenantContext.tenantId));Outbox Pattern
Guaranteed event delivery via transactional outbox:
import { OutboxRepository, RabbitMQEventPublisher, OutboxWorker } from '@tavos/fgsrv-shared-lib';
// Write event in the same DB transaction as business data
await prisma.$transaction(async (tx) => {
await tx.tenant.create({ data: tenantData });
await outboxRepo.createInTransaction(tx, {
aggregateType: 'tenant',
aggregateId: tenant.id,
eventType: 'tenant.created',
payload: tenantData,
});
});Choosing a repository
OutboxRepository is PostgreSQL-only: it claims a batch with
FOR UPDATE SKIP LOCKED, which a MongoDB Prisma client cannot execute.
MongoDB-backed services provide MongoOutboxRepository instead — same surface,
claimed through MongoDB's atomic findAndModify under the same
locked_by/locked_until lease, so OutboxWorker is unchanged:
import { MongoOutboxRepository, OutboxRepository } from '@tavos/fgsrv-shared-lib';
{
provide: OutboxRepository,
useFactory: (prisma: PrismaService) =>
new MongoOutboxRepository(prisma) as unknown as OutboxRepository,
inject: [PrismaService],
}Inbox (consumer idempotency)
Runs a handler exactly once per messageId, so a redelivered message is
skipped rather than reprocessed.
import { PrismaInboxRepository, MongoInboxRepository } from '@tavos/fgsrv-shared-lib';
const executed = await inbox.processIfNotSeen(
message.properties.messageId,
routingKey,
async () => handleEvent(payload),
correlationId,
);
channel.ack(message); // always ack — duplicates are silently skippedPick the implementation that matches the service's database:
| Repository | Engine | Concurrency control |
| --- | --- | --- |
| PrismaInboxRepository | PostgreSQL + Prisma | pg_advisory_xact_lock + ON CONFLICT DO NOTHING |
| RawPgInboxRepository | PostgreSQL, raw pg.Pool | same, without Prisma |
| MongoInboxRepository | MongoDB + Prisma | unique index on message_id; the duplicate-key error is the skip signal |
In every implementation the claim is recorded before the handler runs, so a throwing handler leaves the message marked as seen. Nack without requeue and let the DLQ own it, or delete the claim in a catch block if the handler must be retried.
Audit Interceptor
Tamper-proof audit trail for all mutation operations (POST/PUT/PATCH/DELETE):
import { AuditInterceptor } from '@tavos/fgsrv-shared-lib';
@Module({
providers: [
{ provide: APP_INTERCEPTOR, useClass: AuditInterceptor },
],
})
export class AppModule {}Feature Flags
Dynamic feature toggles via Consul KV with local cache fallback:
import { FeatureFlagService } from '@tavos/fgsrv-shared-lib';
const isEnabled = await featureFlags.isEnabled('capacity_hold_enabled', tenantId);
const config = await featureFlags.getConfig('dynamic_pricing');Graceful Shutdown
import { GracefulShutdownModule, enableGracefulShutdown } from '@tavos/fgsrv-shared-lib';
// In AppModule
@Module({
imports: [GracefulShutdownModule],
})
export class AppModule {}
// In main.ts
const app = await NestFactory.create(AppModule);
enableGracefulShutdown(app);Sub-path Imports
You can import individual modules directly to minimize bundle:
import '@tavos/fgsrv-shared-lib/tracing';
import { LoggingModule } from '@tavos/fgsrv-shared-lib/logging';
import { HealthModule } from '@tavos/fgsrv-shared-lib/health';
import { OutboxRepository } from '@tavos/fgsrv-shared-lib/outbox';
import { MongoInboxRepository } from '@tavos/fgsrv-shared-lib/inbox';
import { TenantMiddleware } from '@tavos/fgsrv-shared-lib/auth';
import { GracefulShutdownModule } from '@tavos/fgsrv-shared-lib/config';Requirements
- Node.js >= 20
- NestJS >= 11
- TypeScript >= 5.7
