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

@vtvlive/interactive-apm

v0.0.5

Published

APM integration package supporting both Elastic APM and OpenTelemetry with NestJS integration

Readme

@vtvlive/interactive-apm

APM integration package supporting both Elastic APM and OpenTelemetry with NestJS integration

Features

  • Dual APM Provider Support: Switch between Elastic APM and OpenTelemetry via environment variable
  • NestJS Integration: Ready-to-use module with dependency injection
  • Standalone Usage: Works without NestJS for vanilla TypeScript/JavaScript projects
  • Flexible Configuration: Configure via environment variables or programmatic options
  • Debug Mode: Comprehensive logging for troubleshooting connection issues
  • OTLP Transport Options: Support for HTTP, gRPC, and PROTO (protobuf over HTTP) transports
  • TypeScript Support: Fully typed with TypeScript
  • Auto Transaction Naming: HTTP requests automatically named as "METHOD /route/path"

Installation

For OpenTelemetry

npm install @vtvlive/interactive-apm @opentelemetry/api @opentelemetry/exporter-trace-otlp-http @opentelemetry/instrumentation-http @opentelemetry/sdk-node @opentelemetry/resources

For gRPC or PROTO transport support:

npm install @opentelemetry/exporter-trace-otlp-grpc @opentelemetry/exporter-trace-otlp-proto

For Elastic APM

npm install @vtvlive/interactive-apm elastic-apm-node

For NestJS Integration

Add the required NestJS dependencies:

npm install @nestjs/common @nestjs/config

Environment Variables

| Variable | Description | Default | | -------------------------------------- | --------------------------------------------------------------------------- | --------------------------------- | | APM_PROVIDER | Provider selection: elastic-apm or opentelemetry | opentelemetry | | APM_DEBUG | Enable debug mode with detailed logging (default: false) | false | | ELASTIC_APM_SERVICE_NAME | Service name for APM | Required | | ELASTIC_APM_SERVER_URL | Elastic APM server URL | http://localhost:8200 | | ELASTIC_APM_SECRET_TOKEN | Secret token for authentication | Optional | | ELASTIC_APM_ENVIRONMENT | Deployment environment | development | | ELASTIC_OTLP_ENDPOINT | OTLP endpoint (OpenTelemetry only) | http://localhost:8200/v1/traces | | ELASTIC_OTLP_TRANSPORT | OTLP transport: http, grpc, or proto | http | | ELASTIC_OTLP_AUTH_TOKEN | OTLP-specific auth token (takes precedence over ELASTIC_APM_SECRET_TOKEN) | Optional | | ELASTIC_OTLP_HEADERS | Additional OTLP headers as JSON string | Optional | | ELASTIC_OTLP_ENABLE_CONSOLE_EXPORTER | Enable console exporter for debugging | false |

Debug Mode

Enable debug mode to see detailed logs about APM initialization, span creation, and export status:

# Enable debug mode
APM_DEBUG=true

# Or in .env file
APM_DEBUG=true

Debug mode provides:

  • Initialization details (service name, endpoint, transport type)
  • Span creation and lifecycle events
  • Export success/failure status
  • HTTP request/response details for troubleshooting

Usage

NestJS Integration

Complete Example with ConfigService

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TracingModule } from '@vtvlive/interactive-apm';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: ['.env.local', '.env'],
    }),

    TracingModule.registerAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => {
        return {
          // Provider selection
          provider: configService.get<string>('APM_PROVIDER', 'opentelemetry'),

          // Common configuration
          serviceName: configService.get<string>('ELASTIC_APM_SERVICE_NAME', 'my-service'),
          environment: configService.get<string>('ELASTIC_APM_ENVIRONMENT', 'development'),

          // OpenTelemetry configuration
          otlpEndpoint: configService.get<string>('ELASTIC_OTLP_ENDPOINT', 'http://localhost:8200/v1/traces'),
          otlpTransport: configService.get<string>('ELASTIC_OTLP_TRANSPORT', 'http'), // 'http' | 'grpc' | 'proto'
          otlpAuthToken: configService.get<string>('ELASTIC_OTLP_AUTH_TOKEN'),
          otlpHeaders: configService.get('ELASTIC_OTLP_HEADERS'),
          enableConsoleExporter: configService.get<string>('ELASTIC_OTLP_ENABLE_CONSOLE_EXPORTER', 'false') === 'true',

          // Elastic APM configuration
          serverUrl: configService.get<string>('ELASTIC_APM_SERVER_URL', 'http://localhost:8200'),
          secretToken: configService.get<string>('ELASTIC_APM_SECRET_TOKEN'),

          // Debug mode - parse boolean from string env var
          debug: ['1', 'true', 'yes'].includes(configService.get<string>('APM_DEBUG', 'false').toLowerCase()),
        };
      },
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

Basic Usage (reads from environment)

// app.module.ts
import { TracingModule } from '@vtvlive/interactive-apm';

@Module({
  imports: [
    TracingModule.registerAsync(),
    // ... other modules
  ],
})
export class AppModule {}

In Controllers

import { Controller, Get } from '@nestjs/common';
import { TracingService } from '@vtvlive/interactive-apm';

@Controller('healthcheck')
export class HealthController {
  constructor(private readonly tracingService: TracingService) {}

  @Get('ping')
  ping() {
    return this.tracingService.startSpanWithParent(
      'healthcheck.ping',
      async (span) => {
        span.setAttribute('healthcheck.type', 'liveness');
        return { message: 'pong' };
      },
      { 'http.method': 'GET', 'http.route': '/healthcheck/ping' }
    );
  }
}

In Services

import { Injectable } from '@nestjs/common';
import { TracingService } from '@vtvlive/interactive-apm';

@Injectable()
export class UserService {
  constructor(
    private readonly tracingService: TracingService,
    private readonly userRepository: UserRepository
  ) {}

  async findById(id: string) {
    return this.tracingService.startSpanWithParent(
      'user.findById',
      async (span) => {
        span.setAttribute('userId', id);

        const user = await this.userRepository.findById(id);
        if (!user) {
          throw new NotFoundException(`User ${id} not found`);
        }

        span.setAttribute('user.exists', 'true');
        return user;
      },
      { 'operation.type': 'read' }
    );
  }

  async create(data: CreateUserDto) {
    return this.tracingService.startSpanWithParent(
      'user.create',
      async (span) => {
        span.setAttribute('user.email', data.email);

        // This nested call creates a child span
        const hashedPassword = await this.tracingService.startSpanWithParent(
          'user.hashPassword',
          async () => await bcrypt.hash(data.password, 10),
          { 'operation.type': 'crypto' }
        );

        const user = await this.userRepository.create({
          ...data,
          password: hashedPassword,
        });

        span.setAttribute('user.created', 'true');
        return user;
      },
      { 'operation.type': 'write' }
    );
  }
}

Standalone Usage (without NestJS)

OpenTelemetry

import { initOpenTelemetry } from '@vtvlive/interactive-apm';

// Initialize at the top of your application (before any imports)
await initOpenTelemetry({
  serviceName: 'my-service',
  otlpEndpoint: 'http://localhost:8200/v1/traces',
  environment: 'production',
  otlpTransport: 'proto', // 'http' | 'grpc' | 'proto'
  enableConsoleExporter: false,
});

// Your application code
import express from 'express';
const app = express();

Elastic APM

import { initElasticApm } from '@vtvlive/interactive-apm';

// Initialize at the top of your application
initElasticApm({
  serviceName: 'my-service',
  serverUrl: 'http://localhost:8200',
  environment: 'production',
  secretToken: 'your-secret-token',
});

// Your application code

API Reference

TracingModule

NestJS module for APM integration.

| Method | Description | | ------------------------ | -------------------------------------------------------------- | | register(options) | Register module with synchronous options | | registerAsync(options) | Register module with asynchronous options (with ConfigService) |

TracingService

Service for tracing operations.

| Method | Description | | -------------------------------------------- | ----------------------------------------------------- | | startSpan(name, attributes?, spanKind?) | Start a new span (must call span.end() manually) | | startSpanWithParent(name, fn, attributes?) | Execute function with automatic tracing (recommended) | | captureError(error) | Capture error to active span | | setAttribute(key, value) | Set attribute on active span | | endSpan(span?) | End a span manually | | shutdown() | Flush and shutdown provider |

SpanKind

Types of spans (used with startSpan):

  • INTERNAL: Internal operation
  • SERVER: Server-side request handler (API endpoints)
  • CLIENT: Client-side call (outgoing HTTP, database)
  • PRODUCER: Message producer
  • CONSUMER: Message consumer

OtlpTransport

Transport options for OpenTelemetry OTLP exporter:

  • HTTP: JSON over HTTP (default)
  • GRPC: gRPC with protobuf
  • PROTO: Protobuf over HTTP (most efficient)

Transaction Naming

HTTP transactions are automatically named in the format METHOD /route/path:

  • Request to GET /api/healthcheck/ping → Transaction: GET /api/healthcheck/ping
  • Request to POST /api/users → Transaction: POST /api/users

This applies to both OpenTelemetry and Elastic APM providers.

Provider Comparison

| Feature | OpenTelemetry | Elastic APM | | -------------------- | ---------------------------- | ---------------- | | Transport Options | HTTP, gRPC, PROTO | HTTP/HTTPS | | Auto Instrumentation | HTTP, Express (configurable) | Built-in | | Debug Logging | Comprehensive | Comprehensive | | Transaction Naming | Auto + Custom | Auto + Custom | | Standard | W3C Trace Context | Elastic APM spec |

Troubleshooting

No traces appearing in APM

  1. Check if APM_DEBUG=true is set to see detailed logs
  2. Verify endpoint URL is correct
  3. Check network connectivity to APM server
  4. Verify authentication token

Using npm link for local development

# In the package directory
cd /path/to/interactive-apm
npm link

# In your project directory
cd /path/to/your-project
npm link @vtvlive/interactive-apm

Note: After making changes to the package, rebuild it:

cd /path/to/interactive-apm
npm run build

Testing

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage

# Run only unit tests
npm run test:unit

# Run only integration tests
npm run test:integration

Development

# Build the package
npm run build

# Build in watch mode
npm run build:watch

# Run tests
npm test

# Clean build artifacts
npm run clean

License

MIT

Author

VTVLive


For more information: