Nestlib – Nest APM – helpers for APM (Application Performance Monitoring) in NestJS projects
npm install @nestlib/apm





❤️🔥 Elastic APM integration for NestJS applications ❤️🔥

📊 Elastic APM integration: Full-featured APM agent integration for NestJS
🎯 Transaction tracking: Automatic and manual transaction management
🔍 Span tracking: Detailed performance monitoring with spans
🎨 Decorators: @ApmTrack and @ApmSpan for easy integration
🗄️ Specialized tracking: Elasticsearch, MongoDB, LLM, RabbitMQ
💉 DI Support: Dependency injection with @InjectApmService decorator
``bashpnpm
pnpm add @nestlib/apm elastic-apm-node
`typescript
import { Module } from '@nestjs/common';
import { ApmModule } from '@nestlib/apm';@Module({
imports: [
ApmModule.forRoot({
active: true,
config: {
serviceName: 'my-service',
serverUrl: 'http://apm-server:8200',
},
}),
],
})
export class AppModule {}
`The module will automatically load APM configuration from
.env file, environment variables, or provided options.✨ Features
- 📊 Elastic APM integration for NestJS applications
- 🎯 Automatic transaction tracking with decorators
- 🔍 Span tracking for detailed performance monitoring
- 🗄️ Specialized tracking for Elasticsearch, MongoDB, LLM, and RabbitMQ
- 🎨 Decorator-based API for easy integration
- 🔌 Interceptor support for automatic HTTP request tracking
- ⚙️ Flexible configuration via module options, config files, or environment variables
- 🛡️ Graceful degradation when APM is disabled
- 📝 Full TypeScript support with type definitions
- 🔀 Async module registration with
forRootAsync📖 Usage
$3
#### Inject and Access APM Service
`typescript
import { Injectable } from '@nestjs/common';
import { ApmService, InjectApmService } from '@nestlib/apm';@Injectable()
export class MyService {
constructor(
@InjectApmService() private readonly apm: ApmService,
) {}
async getUser(id: string) {
return this.apm.runTransaction('getUser', 'request', async () => {
// Your logic here
return { id, name: 'John Doe' };
});
}
}
`#### Transaction with Labels and Context
`typescript
async createOrder(userId: string, items: any[]) {
return this.apm.runTransaction(
'createOrder',
'request',
async () => {
// Your order creation logic
return { orderId: '12345' };
},
{
labels: { userId, itemCount: items.length },
context: { order: { items } },
},
);
}
`#### Span Tracking
`typescript
async performDatabaseQuery() {
return this.apm.runSpan(
'database-query',
'db',
async () => {
// Database query logic
},
{
subtype: 'postgresql',
labels: { 'db.name': 'mydb' },
},
);
}
`$3
#### @ApmTrack Decorator
Automatically track methods as transactions:
`typescript
import { Injectable } from '@nestjs/common';
import { ApmTrack } from '@nestlib/apm';@Injectable()
export class DataService {
@ApmTrack('fetchData', 'request')
async fetchData() {
// Your logic here
}
@ApmTrack() // Uses method name automatically
async processData() {
// Your logic here
}
}
`#### @ApmSpan Decorator
Track methods as spans within transactions:
`typescript
import { Injectable } from '@nestjs/common';
import { ApmSpan } from '@nestlib/apm';@Injectable()
export class DatabaseService {
@ApmSpan('database-query', 'db', 'postgresql')
async queryDatabase() {
// Database query logic
}
@ApmSpan() // Uses method name automatically
async cacheLookup() {
// Cache lookup logic
}
}
`$3
#### Elasticsearch
`typescript
async searchUsers(query: string) {
return this.apm.trackElasticsearchQuery(
'users-index',
'search',
async () => {
return await elasticsearchClient.search({ ... });
},
{ query },
);
}
`#### MongoDB
`typescript
async findUsers(filter: any) {
return this.apm.trackMongoQuery(
'users',
'find',
async () => {
return await mongoCollection.find(filter).toArray();
},
filter,
);
}
`#### LLM Requests
`typescript
async generateText(prompt: string) {
return this.apm.trackLlmRequest(
'gpt-4',
'completion',
async () => {
return await llmClient.complete(prompt);
},
{ text: prompt, tokenCount: 100 },
);
}
`#### RabbitMQ Messages
`typescript
async handleMessage(queue: string, routingKey: string, data: any) {
return this.apm.trackRabbitMqMessage(
queue,
routingKey,
async (transaction) => {
if (transaction) {
transaction.addLabels({ messageId: data.id });
}
// Process message
},
data,
);
}
`$3
Use
forRootAsync when you need to load configuration dynamically:`typescript
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { ApmModule } from '@nestlib/apm';@Module({
imports: [
ApmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
active: config.get('APM_ACTIVE') === 'true',
config: {
serviceName: config.get('SERVICE_NAME'),
environment: config.get('NODE_ENV'),
serverUrl: config.get('APM_SERVER_URL'),
},
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}
`$3
#### ElasticsearchApmInterceptor
`typescript
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { ElasticsearchApmInterceptor } from '@nestlib/apm';@Controller('search')
@UseInterceptors(ElasticsearchApmInterceptor)
export class SearchController {
@Get()
async search() {
// Elasticsearch query
}
}
`$3
`env
Enable/disable APM
ELASTIC_APM_ACTIVE=trueService identification
ELASTIC_APM_SERVICE_NAME=my-service
ELASTIC_APM_ENVIRONMENT=productionAPM Server connection
ELASTIC_APM_SERVER_URL=http://apm-server:8200
ELASTIC_APM_SECRET_TOKEN=your-secret-token
`📚 API Reference
$3
####
ApmModule.forRoot(options?: ApmModuleOptions)Initialize the APM module synchronously.
Options:
`typescript
interface ApmModuleOptions {
config?: AgentConfigOptions; // Elastic APM configuration
active?: boolean; // Whether APM is active
serviceName?: string; // Service name
environment?: string; // Environment name
}
`####
ApmModule.forRootAsync(options?: ApmModuleAsyncOptions)Initialize the APM module asynchronously.
`typescript
interface ApmModuleAsyncOptions {
imports?: any[]; // Modules to import
inject?: InjectionToken[]; // Dependencies to inject into useFactory
useFactory?: (...args: any[]) => Promise | ApmModuleOptions;
}
`$3
Service for programmatic APM tracking.
`typescript
// Run a transaction
await apmService.runTransaction('name', 'type', fn, options?);// Run a span within current transaction
await apmService.runSpan('name', 'type', fn, options?);
// Specialized tracking
await apmService.trackElasticsearchQuery(index, operation, fn, queryBody?);
await apmService.trackMongoQuery(collection, operation, fn, query?);
await apmService.trackLlmRequest(model, operation, fn, options?);
await apmService.trackRabbitMqMessage(queue, routingKey, fn, messageData?);
// Labels and context
apmService.addLabels({ key: 'value' });
apmService.setCustomContext({ custom: 'data' });
// Error tracking
apmService.captureError(error, context?);
// Check if APM is running
apmService.isStarted();
`$3
Decorator for injecting ApmService.
`typescript
// Inject default APM service
constructor(@InjectApmService() private apm: ApmService) {}// Inject namespaced APM service
constructor(@InjectApmService('custom') private apm: ApmService) {}
`$3
#### @ApmTrack(name?, type?)
Decorator for automatic transaction tracking.
-
name - Transaction name (optional, defaults to ClassName.methodName)
- type - Transaction type (default: 'custom')#### @ApmSpan(name?, type?, subtype?)
Decorator for span tracking.
-
name - Span name (optional, defaults to ClassName.methodName)
- type - Span type (default: 'custom')
- subtype - Span subtype (optional)🔧 Configuration
$3
1. Module options (highest priority)
2. Config file (
.env via @lsk4/config)
3. Environment variables (lowest priority)$3
The module automatically loads configuration from
.env files:`env
.env
APM_ACTIVE=true
APM_SERVICE_NAME=my-service
APM_ENVIRONMENT=production
APM_SERVER_URL=http://apm-server:8200
``If APM is not started or disabled, all methods work without tracking. Your application continues to function even if APM is unavailable.
MIT © Igor Suvorov
* GitHub Repository
* Issues
* Telegram
---
@nestlib/apm – _Elastic APM integration for NestJS_ 📊