npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@stefaninigo/core

v1.2.0

Published

Enterprise-grade NestJS library for the StefaniniGo microservice ecosystem

Readme

stefaninigo

Enterprise-grade NestJS library for the StefaniniGo microservice ecosystem. Provides database access, file storage, caching, messaging, real-time transport, event-driven architecture, geolocation utilities, health checks, and standardized request/response handling -- all through a unified Strategy + DynamicModule pattern.

Design Philosophy

  • Transversal -- shared foundation for all microservices in the ecosystem.
  • Generic -- no business logic; only infrastructure concerns.
  • Provider-agnostic -- every infrastructure module defines a service interface; concrete implementations (strategies) are swappable without touching consumer code.
  • Easy integration -- forRootAsync() one-liner registration, @Inject('token') consumption.

Installation

npm install stefaninigo

Quick Start

// app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import {
  DatabaseModule,
  StorageModule,
  CacheModule,
  EventBusModule,
  HealthModule,
  TraceIdMiddleware,
  RequestContextMiddleware,
  LoggingInterceptor,
  ResponseInterceptor,
  ProblemDetailsInterceptor,
} from 'stefaninigo';
import { APP_INTERCEPTOR } from '@nestjs/core';
import configuration from './config/configuration';

@Module({
  imports: [
    ConfigModule.forRoot({ load: [configuration], isGlobal: true }),

    // Database -- MongoDB provider with injection token 'db'
    DatabaseModule.forRootAsync([
      { name: 'db', provider: DatabaseModule.PROVIDERS.MONGODB },
    ]),

    // Storage -- S3 provider with injection token 'storage'
    StorageModule.forRootAsync([
      { name: 'storage', provider: StorageModule.PROVIDERS.S3 },
    ]),

    // Cache -- Redis provider with injection token 'cache'
    CacheModule.forRootAsync([
      { name: 'cache', provider: CacheModule.PROVIDERS.REDIS },
    ]),

    // Event bus -- global module, no forRoot needed
    EventBusModule,

    // Health checks
    HealthModule.forRoot({
      config: {
        serviceName: 'my-service',
        version: '1.0.0',
        database: {
          token: 'db',
          collection: 'health',
          timeoutMs: 3000,
        },
      },
    }),
  ],
  providers: [
    // Interceptor chain (order matters -- first registered runs outermost)
    { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor },
    { provide: APP_INTERCEPTOR, useClass: ResponseInterceptor },
    { provide: APP_INTERCEPTOR, useClass: ProblemDetailsInterceptor },
  ],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(TraceIdMiddleware, RequestContextMiddleware)
      .forRoutes('*');
  }
}
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import {
  createStandardValidationPipe,
  setupSwagger,
  setupCors,
} from 'stefaninigo';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(createStandardValidationPipe());
  app.setGlobalPrefix('api/v1/my-service');
  setupCors(app);
  setupSwagger(app, {
    title: 'My Service',
    description: 'API docs',
    version: '1.0.0',
  });

  await app.listen(3000);
}
bootstrap();

Modules

| Module | Pattern | Providers | Description | |--------|---------|-----------|-------------| | DatabaseModule | Strategy + DynamicModule | MongoDB, DynamoDB, PostgreSQL | Provider-agnostic data access layer | | StorageModule | Strategy + DynamicModule | S3 | File upload, download, presigned URLs | | CacheModule | Strategy + DynamicModule | Redis, Local (in-memory) | Key-value caching with TTL | | MessagingModule | Strategy + DynamicModule | SNS, SQS | Pub/sub and point-to-point messaging | | TransportModule | Strategy + DynamicModule | AWS API Gateway WebSocket | Real-time bidirectional communication | | EventBusModule | @Global module | log, SNS, SQS | Toggleable domain event publishing | | HealthModule | DynamicModule | -- | K8s liveness, readiness, detailed health | | Core | Interceptors + Middleware | -- | Tracing, logging, RFC 7807 errors, response format | | Config | Service | -- | Centralized config resolver for all modules | | Geo | Pure utilities | -- | GeoJSON types, Haversine distance, bounding boxes |

Architecture

Strategy Pattern

Every infrastructure module (DatabaseModule, StorageModule, CacheModule, MessagingModule, TransportModule) follows the same pattern:

  1. A service interface defines the contract (e.g. DatabaseService, StorageService, CacheServiceInterface).
  2. Concrete strategies implement that interface per provider (e.g. MongoDBService, DynamoDBService, PostgreService).
  3. The module exposes forRootAsync(providers[]) which takes an array of { name, provider } configs.
  4. Each entry creates a named NestJS provider you inject with @Inject('name').
// Register two databases with different tokens
DatabaseModule.forRootAsync([
  { name: 'mainDb', provider: DatabaseModule.PROVIDERS.MONGODB },
  { name: 'analyticsDb', provider: DatabaseModule.PROVIDERS.MONGODB, configKey: 'analytics' },
  { name: 'dynamoDb', provider: DatabaseModule.PROVIDERS.DYNAMODB },
])

// Inject in a service
constructor(@Inject('mainDb') private readonly db: DatabaseService) {}

Interceptor Chain

Interceptors execute in registration order on the request path and in reverse on the response/error path:

Request Flow:
  Client --> LoggingInterceptor --> ResponseInterceptor --> ProblemDetailsInterceptor --> Controller

Response Flow (success):
  Controller --> ProblemDetailsInterceptor (pass-through) --> ResponseInterceptor (wraps: {success, message}) --> LoggingInterceptor (logs duration) --> Client

Error Flow:
  Controller throws --> ProblemDetailsInterceptor (converts to RFC 7807) --> ResponseInterceptor (pass-through if ProblemDetails) --> LoggingInterceptor (logs error) --> Client

| Interceptor | Responsibility | |-------------|---------------| | LoggingInterceptor | Logs request entry/exit, duration, errors. Includes traceId, controller, handler. Emits structured metrics. | | ResponseInterceptor | Wraps successful responses as { success: true, message: data }. Wraps non-ProblemDetails errors as { success: false, error, message, statusCode }. | | ProblemDetailsInterceptor | Converts all errors to RFC 7807 ProblemDetails format. Sanitizes sensitive body fields. Adds debugInfo in development mode. |

Distributed Tracing

Full request lifecycle tracing through AsyncLocalStorage:

Incoming Request
  |
  v
TraceIdMiddleware          -- reads/generates X-Trace-ID, attaches to req & res header
  |
  v
RequestContextMiddleware   -- creates RequestContext in AsyncLocalStorage (traceId, userId, language, startTime)
  |
  v
TracedLogger               -- extends NestJS Logger, auto-prefixes [traceId] to every log line
  |
  v
LoggingInterceptor         -- reads traceId for structured logging
  |
  v
ProblemDetailsInterceptor  -- embeds traceId in error responses
// Use TracedLogger in any service for automatic traceId injection
import { TracedLogger } from 'stefaninigo';

export class TicketService {
  private readonly logger = new TracedLogger(TicketService.name);

  findOne(id: string) {
    this.logger.log(`Finding ticket ${id}`);
    // Output: [a1b2c3d4-...] Finding ticket TK-001
  }
}

// Or access context directly
import { getRequestContext, getTraceId } from 'stefaninigo';

const ctx = getRequestContext(); // { traceId, userId, language, startTime }
const traceId = getTraceId();   // shortcut

Event-Driven Architecture

EventBusModule is a @Global module with 3 publishing strategies, toggled via environment variables:

EventBusService.publish(event)
  |
  |-- strategy = 'log'  --> Logger output (default, zero infra)
  |-- strategy = 'sns'  --> AWS SNS topic (fan-out)
  |-- strategy = 'sqs'  --> AWS SQS queue (point-to-point)
import { EventBusService } from 'stefaninigo';
import { randomUUID } from 'crypto';

@Injectable()
export class TicketService {
  constructor(private readonly eventBus: EventBusService) {}

  async create(dto: CreateTicketDto) {
    const ticket = await this.repo.create(dto);

    await this.eventBus.publish({
      eventId: randomUUID(),
      eventType: 'ticket.created',
      timestamp: new Date().toISOString(),
      providerId: dto.providerId,
      source: 'tickets-service',
      data: { ticketId: ticket.id, clientId: dto.clientId },
    });

    return ticket;
  }
}

The library provides typed domain event interfaces for: tickets, routes, geofence, technicians, inventory, clients, locations, contacts, categories, and config. See events/types/domain-events.ts.

Environment Variables

Database

| Variable | Provider | Description | |----------|----------|-------------| | database.mongodb.uri | MongoDB | Connection URI | | database.mongodb.dbName | MongoDB | Default database name | | database.mongodb.updateAllFields | MongoDB | Replace vs merge on update | | database.mongodb.returnUpdatedDocument | MongoDB | Return doc after update | | database.dynamodb.region | DynamoDB | AWS region | | database.dynamodb.accessKeyId | DynamoDB | AWS access key | | database.dynamodb.secretAccessKey | DynamoDB | AWS secret key | | database.postgres.host | PostgreSQL | Host | | database.postgres.port | PostgreSQL | Port | | database.postgres.username | PostgreSQL | Username | | database.postgres.password | PostgreSQL | Password | | database.postgres.database | PostgreSQL | Database name |

Storage

| Variable | Provider | Description | |----------|----------|-------------| | storage.s3.region | S3 | AWS region | | storage.s3.accessKeyId | S3 | AWS access key | | storage.s3.secretAccessKey | S3 | AWS secret key | | storage.s3.bucket | S3 | Bucket name | | storage.s3.containerPath | S3 | Default key prefix | | storage.s3.expiresIn | S3 | Presigned URL expiry in seconds (default: 3600) |

Cache

| Variable | Provider | Description | |----------|----------|-------------| | cache.redis.host | Redis | Redis host | | cache.redis.port | Redis | Redis port |

Messaging

| Variable | Provider | Description | |----------|----------|-------------| | message.sns.region | SNS | AWS region | | message.sns.accessKeyId | SNS | AWS access key | | message.sns.secretAccessKey | SNS | AWS secret key | | message.sqs.region | SQS | AWS region | | message.sqs.accessKeyId | SQS | AWS access key | | message.sqs.secretAccessKey | SQS | AWS secret key | | message.sqs.queueUrl | SQS | Default queue URL | | message.sqs.MaxNumberOfMessages | SQS | Max messages per poll (default: 10) | | message.sqs.WaitTimeSeconds | SQS | Long poll wait (default: 20) | | message.sqs.pollInterval | SQS | Poll interval ms (default: 10000) |

Transport

| Variable | Provider | Description | |----------|----------|-------------| | transport.aws_ws.domain | AWS WebSocket | API Gateway WebSocket endpoint |

EventBus

| Variable | Default | Description | |----------|---------|-------------| | EVENTS_ENABLED | false | Enable/disable event publishing | | EVENTS_STRATEGY | log | Strategy: log, sns, or sqs | | EVENTS_SNS_TOPIC_ARN | -- | SNS topic ARN (when strategy=sns) | | EVENTS_SQS_QUEUE_URL | -- | SQS queue URL (when strategy=sqs) |

Core

| Variable | Default | Description | |----------|---------|-------------| | NODE_ENV | development | Controls debug info in error responses | | CORS_ORIGINS | * | Comma-separated allowed origins |

Module Documentation

Each module has its own ARCHITECTURE.md with detailed API reference, usage examples, and configuration:

Tech Stack

| Dependency | Version | Purpose | |------------|---------|---------| | NestJS | 11.x | Framework | | TypeScript | 5.x | Language | | MongoDB driver | 6.x | MongoDB provider | | pg | 8.x | PostgreSQL provider | | AWS SDK v3 | 3.x | S3, SNS, SQS, DynamoDB, API Gateway | | ioredis | 5.x | Redis provider | | axios | 1.x | HTTP transport | | ws | 8.x | WebSocket transport |

License

UNLICENSED -- Internal use only.