A comprehensive helper library for Node.js applications that provides enterprise-grade utilities for AWS Secrets Manager integration, Elasticsearch/OpenSearch logging, Slack notifications, Express.js middleware, and NestJS integration with advanced features.
- AWS Secrets Manager Integration: Secure secret loading with retry logic and IAM role support
- Elasticsearch/OpenSearch Logging: Enterprise-grade logging with transaction tracking
- Pino Logger Integration: High-performance structured logging
- Slack Notifications: Complete Slack integration with API and webhook support, enhanced block validation, and automatic error handling
- NestJS Compatibility: Native integration with decorators, interceptors, guards, and exception filters
- Express Middleware: Performance monitoring and transaction logging
- TypeScript Support: Full type safety and IntelliSense support
- AWS IAM Integration: Seamless authentication with AWS services
npm install @starbemtech/star-node-stack-helper
# or
pnpm add @starbemtech/star-node-stack-helper
# or
yarn add @starbemtech/star-node-stack-helper- Node.js >= 18
- TypeScript (optional but recommended)
- AWS Secrets Manager
- Elasticsearch/OpenSearch Logging
- Pino Logger
- Slack Notifications
- NestJS Compatibility
- Express Middleware
- TypeScript Types
- Examples
- API Reference
import {
loadSecrets,
testSavedSecrets,
isRunningOnAWS,
getAWSRegion,
} from '@starbemtech/star-node-stack-helper'
// Load secrets from AWS Secrets Manager
const secrets = await loadSecrets({
region: 'us-east-1',
secretName: ['prod/database/credentials', 'prod/api/keys'],
})
// Test if secrets were loaded correctly
testSavedSecrets()
// Check if running on AWS
const isAWS = isRunningOnAWS() // true if running on AWS Lambda or EC2
// Get AWS region
const region = getAWSRegion() // 'us-east-1' or undefinedimport {
loadSecrets,
SecretConfig,
RetryConfig,
} from '@starbemtech/star-node-stack-helper'
const retryConfig: RetryConfig = {
maxRetries: 3,
retryDelay: 1000,
exponentialBackoff: true,
}
const secretConfig: SecretConfig = {
region: 'us-east-1',
secretName: ['prod/database/credentials'],
retry: retryConfig,
credentials: {
accessKeyId: 'your-access-key',
secretAccessKey: 'your-secret-key',
},
}
const secrets = await loadSecrets(secretConfig)After loading secrets, they are automatically set as environment variables:
// If secret contains: { "DB_HOST": "localhost", "DB_PORT": "5432" }
// These become available as:
console.log(process.env.DB_HOST) // 'localhost'
console.log(process.env.DB_PORT) // '5432'import {
ElasticLogger,
LoggerConfig,
} from '@starbemtech/star-node-stack-helper'
const config: LoggerConfig = {
node: 'https://your-opensearch-cluster.us-east-1.es.amazonaws.com',
service: 'my-service',
environment: 'production',
index: 'application-logs',
region: 'us-east-1',
authType: 'aws', // or 'basic' with username/password
}
const logger = new ElasticLogger(config)// Basic logging
await logger.log('info', 'User logged in successfully', {
userId: '123',
sessionId: 'sess_456',
ip: '192.168.1.1',
})
// Different log levels
await logger.log('debug', 'Debug information', { step: 'validation' })
await logger.log('warn', 'Warning message', { threshold: 80 })
await logger.log('error', 'Error occurred', {
error: 'Database connection failed',
})import { LogTransaction } from '@starbemtech/star-node-stack-helper'
const transaction: LogTransaction = {
name: 'user-registration',
microservice: 'auth-service',
operation: 'create-user',
status: 'success',
duration: 1500,
context: {
userId: '123',
email: 'user@example.com',
plan: 'premium',
},
requestMeta: {
method: 'POST',
path: '/api/users',
ip: '192.168.1.1',
userAgent: 'Mozilla/5.0...',
},
}
await logger.logTransaction(transaction)// Search system logs
const systemLogs = await logger.getSystemLogs('user login')
// Search transaction logs
const transactionLogs = await logger.getLogsTransactions('user-registration')
// Multiple queries
const logs = await logger.getSystemLogs(['error', 'database', 'timeout'])// Check cluster health
const isHealthy = await logger.healthCheck()
// Test connection
await logger.testConnection()
// Flush indices
await logger.flush()
// Recreate index (useful for schema changes)
await logger.recreateIndex()import {
createPinoLogger,
createHttpLogger,
pinoLogContext,
} from '@starbemtech/star-node-stack-helper'
const logger = createPinoLogger({
serviceName: 'my-service',
environment: 'production',
logLevel: 'info',
})
// Create HTTP middleware
const httpLogger = createHttpLogger(logger)import express from 'express'
const app = express()
// Add HTTP logging middleware
app.use(httpLogger)
app.get('/api/users', (req, res) => {
// Logs are automatically captured
res.json({ users: [] })
})// Basic structured logging
logger.info('User action completed', {
userId: '123',
action: 'profile_update',
timestamp: new Date().toISOString(),
})
// Using context helpers
const requestContext = pinoLogContext.request(req, {
userId: '456',
sessionId: 'sess_789',
})
logger.info('User performed action', {
...requestContext,
action: 'file_upload',
fileSize: 1024,
fileName: 'document.pdf',
})
// Performance logging
const startTime = Date.now()
// ... perform operation
const duration = Date.now() - startTime
const performanceContext = pinoLogContext.performance('file_upload', duration, {
fileSize: 1024,
fileName: 'document.pdf',
})
logger.info('Operation completed', {
...requestContext,
...performanceContext,
success: true,
})
// Proxy/API Gateway logging
const proxyContext = pinoLogContext.proxy(
'https://api.example.com',
'user-service',
{
method: 'GET',
endpoint: '/users/123',
responseTime: 150,
}
)
logger.info('API Gateway request', proxyContext)
// Error logging
try {
// ... risky operation
} catch (error) {
const errorContext = pinoLogContext.error(error, {
operation: 'file_upload',
retryable: true,
})
logger.error('Operation failed', {
...requestContext,
...errorContext,
severity: 'high',
})
}const logger = createPinoLogger({
serviceName: 'my-service',
environment: 'production',
logLevel: 'debug',
customFormatters: {
level: (label) => ({ level: label.toUpperCase() }),
log: (object) => ({ ...object, custom: true }),
},
customSerializers: {
req: (req) => ({ method: req.method, url: req.url }),
res: (res) => ({ statusCode: res.statusCode }),
err: (err) => ({ message: err.message, stack: err.stack }),
},
redactPaths: ['password', 'token', 'secret'],
})The library provides comprehensive Slack integration with support for both the official Slack API and webhooks, including formatted messages, interactive buttons, and attachments. Now with enhanced block validation and error handling to prevent "invalid_blocks" errors.
import {
sendSlackMessage,
SlackNotifier,
createSectionBlock,
createButtonElement,
createActionBlock,
} from '@starbemtech/star-node-stack-helper'
// Simple message
const response = await sendSlackMessage(
{
channel: '#general',
text: 'Hello! This is a test notification.',
username: 'My Bot',
icon_emoji: ':robot_face:',
},
{
config: {
token: 'xoxb-your-bot-token-here',
defaultChannel: '#general',
botName: 'My Bot',
},
failSilently: true,
}
)
console.log('Message sent:', response.ok)import { sendSlackWebhook } from '@starbemtech/star-node-stack-helper'
const response = await sendSlackWebhook(
{
text: 'Hello via webhook!',
channel: '#general',
username: 'Webhook Bot',
},
{
webhookUrl: 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL',
defaultChannel: '#general',
botName: 'Webhook Bot',
}
)The SlackNotifier class provides a convenient way to manage Slack notifications:
import { SlackNotifier } from '@starbemtech/star-node-stack-helper'
const notifier = new SlackNotifier(
{
token: 'xoxb-your-bot-token-here',
defaultChannel: '#notifications',
botName: 'System Notifications',
},
{
failSilently: true,
retryConfig: {
maxAttempts: 5,
delayMs: 2000,
},
}
)
// Send different types of messages
await notifier.sendMessage('Simple message')
await notifier.sendSuccess('Operation completed successfully!')
await notifier.sendWarning('Attention: Low resources detected')
await notifier.sendError('Critical error: Database connection failed')import {
sendSlackMessage,
createSectionBlock,
createDividerBlock,
createButtonElement,
createActionBlock,
} from '@starbemtech/star-node-stack-helper'
const blocks = [
createSectionBlock('*π New Deploy Completed!*', { type: 'mrkdwn' }),
createDividerBlock(), // Use divider instead of empty section blocks
createSectionBlock('Deployment Details', {
fields: [
{ type: 'mrkdwn', text: '*Environment:*' },
{ type: 'mrkdwn', text: 'Production' },
{ type: 'mrkdwn', text: '*Version:*' },
{ type: 'mrkdwn', text: 'v1.2.3' },
{ type: 'mrkdwn', text: '*Status:*' },
{ type: 'mrkdwn', text: 'β
Success' },
],
}),
createActionBlock([
createButtonElement('View Logs', 'view_logs', {
style: 'primary',
action_id: 'view_logs_btn',
}),
createButtonElement('Rollback', 'rollback', {
style: 'danger',
action_id: 'rollback_btn',
}),
]),
]
const response = await sendSlackMessage(
{
channel: '#deployments',
text: 'New deploy completed successfully!',
blocks,
},
{
config: {
token: 'xoxb-your-bot-token-here',
},
}
)The library now includes automatic block validation and sanitization to prevent "invalid_blocks" errors:
import {
SlackNotifier,
createSectionBlock,
} from '@starbemtech/star-node-stack-helper'
const notifier = new SlackNotifier({
token: 'xoxb-your-bot-token-here',
defaultChannel: '#notifications',
})
// The sendFormattedMessage method now automatically:
// 1. Validates block structure
// 2. Sanitizes text content (removes control characters, escapes special chars)
// 3. Truncates text that exceeds Slack's 3000 character limit
// 4. Filters out invalid blocks
// 5. Falls back to simple text message if all blocks are invalid
await notifier.sendFormattedMessage([
createSectionBlock('*System Status*', { type: 'mrkdwn' }),
createSectionBlock('β
All systems operational'), // Text is automatically sanitized
createSectionBlock(''), // Empty blocks are automatically handled
])- Text Sanitization: Automatically removes control characters and escapes special characters for mrkdwn
- Length Validation: Truncates text that exceeds Slack's 3000 character limit
- Structure Validation: Ensures blocks have required properties and valid types
- Empty Block Handling: Replaces empty section blocks with proper dividers
- Error Recovery: Falls back to simple text messages if all blocks are invalid
- Detailed Logging: Warns about filtered blocks for debugging
import { createSlackAttachment } from '@starbemtech/star-node-stack-helper'
const attachment = createSlackAttachment({
color: 'danger',
title: 'π¨ System Alert',
text: 'The system detected a critical issue that requires immediate attention.',
fields: [
{ title: 'Service', value: 'API Gateway', short: true },
{ title: 'Error', value: 'Connection timeout', short: true },
{ title: 'Timestamp', value: new Date().toISOString(), short: false },
],
ts: Math.floor(Date.now() / 1000),
})
const response = await sendSlackMessage(
{
channel: '#alerts',
text: 'Critical system alert',
attachments: [attachment],
},
{
config: {
token: 'xoxb-your-bot-token-here',
},
}
)import { sendSlackMessage } from '@starbemtech/star-node-stack-helper'
try {
const response = await sendSlackMessage(
{
channel: '#errors',
text: 'This message will be retried multiple times on failure',
},
{
config: {
token: 'xoxb-your-bot-token-here',
},
retryConfig: {
maxAttempts: 3,
delayMs: 1000,
},
failSilently: false, // Will throw error if it fails
}
)
console.log('Message sent successfully:', response)
} catch (error) {
console.error('Failed to send message after all attempts:', error)
}If you encounter the "invalid_blocks" error, the library now automatically handles most common causes:
// β Problematic code (now automatically fixed)
const blocks = [
createSectionBlock(''), // Empty blocks are now handled
createSectionBlock('Very long text...'), // Text is automatically truncated
createSectionBlock('Text with special chars <>&'), // Characters are escaped
]
// β
Recommended approach
const blocks = [
createSectionBlock('*Title*', { type: 'mrkdwn' }),
createDividerBlock(), // Use dividers for separation
createSectionBlock('Content with proper formatting'),
]The library now includes an optional channel membership check to verify if the bot is in the channel before sending messages:
// Enable channel membership check (disabled by default)
await sendSlackMessage(
{
channel: '#general',
text: 'Hello!',
},
{
config: {
token: 'xoxb-your-token',
},
checkChannelMembership: true, // Enable channel check
failSilently: true,
}
)
// Disable channel membership check (default behavior)
await sendSlackMessage(
{
channel: '#general',
text: 'Hello!',
},
{
config: {
token: 'xoxb-your-token',
},
checkChannelMembership: false, // Disable channel check (default)
}
)Note: The SlackNotifier class has channel membership check disabled by default for better performance and to avoid unnecessary API calls.
- Use
createDividerBlock()instead of empty section blocks - Keep text content under 3000 characters (automatically handled)
- Use proper mrkdwn formatting for rich text
- Handle errors gracefully with
failSilently: true - Disable channel membership check if you're sure the bot is in the channel
const notifier = new SlackNotifier(
{
token: 'xoxb-your-token',
defaultChannel: '#notifications',
},
{
failSilently: true, // Prevents crashes on Slack API errors
retryConfig: {
maxAttempts: 3,
delayMs: 1000,
},
}
)- Go to api.slack.com/apps
- Create a new app or select an existing one
- Navigate to "OAuth & Permissions"
- Add the following scopes:
chat:write- Send messageschat:write.public- Send messages to public channelschat:write.customize- Customize bot name and icon
- Install the app to your workspace
- Copy the "Bot User OAuth Token" (starts with
xoxb-)
- Go to api.slack.com/apps
- Create a new app or select an existing one
- Navigate to "Incoming Webhooks"
- Activate "Activate Incoming Webhooks"
- Click "Add New Webhook to Workspace"
- Select the channel and copy the webhook URL
A biblioteca Γ© totalmente compatΓvel com NestJS, oferecendo decorators, interceptors, guards e exception filters para integraΓ§Γ£o nativa com o framework.
npm install @starbemtech/star-node-stack-helper @nestjs/common @nestjs/core rxjs
# ou
pnpm add @starbemtech/star-node-stack-helper @nestjs/common @nestjs/core rxjsimport { Module } from '@nestjs/common'
import { APP_INTERCEPTOR, APP_FILTER } from '@nestjs/core'
import {
ElasticLogger,
LoggerConfig,
TransactionLogInterceptor,
LogExceptionFilter,
} from '@starbemtech/star-node-stack-helper'
const elasticConfig: LoggerConfig = {
node: 'https://your-opensearch-endpoint.com',
service: 'my-service',
environment: 'development',
index: 'transaction-logs',
region: 'us-east-1',
authType: 'aws',
}
const elasticLogger = new ElasticLogger(elasticConfig)
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useValue: new TransactionLogInterceptor({
microservice: 'my-service',
operation: 'api-request',
elasticLogger,
}),
},
{
provide: APP_FILTER,
useClass: LogExceptionFilter,
},
],
})
export class AppModule {}import { Controller, Get, Post, Body } from '@nestjs/common'
import {
Log,
LogPerformance,
LogError,
LogCritical,
} from '@starbemtech/star-node-stack-helper'
@Controller('users')
export class UsersController {
@Get()
@Log('info', 'Fetching all users')
async getUsers() {
return [{ id: 1, name: 'JoΓ£o' }]
}
@Post()
@LogPerformance('user-creation')
async createUser(@Body() userData: any) {
// OperaΓ§Γ£o que serΓ‘ medida automaticamente
return { id: 1, ...userData }
}
@Get('error')
@LogError('Failed to fetch user')
async getUserWithError() {
throw new Error('User not found')
}
@Get('critical')
@LogCritical('Critical system failure')
async criticalOperation() {
// OperaΓ§Γ£o crΓtica que serΓ‘ logada com nΓvel crΓtico
return { status: 'critical' }
}
}import { Controller, Post, Body } from '@nestjs/common'
import { TransactionLog } from '@starbemtech/star-node-stack-helper'
@Controller('transactions')
export class TransactionController {
@Post('payment')
@TransactionLog({
microservice: 'payment-service',
operation: 'process-payment',
elasticLogger,
})
async processPayment(@Body() paymentData: any) {
// TransaΓ§Γ£o serΓ‘ automaticamente logada
return { transactionId: 'tx_123', status: 'success' }
}
}import { Controller, Get, UseInterceptors } from '@nestjs/common'
import { TransactionLogInterceptor } from '@starbemtech/star-node-stack-helper'
@Controller('api')
@UseInterceptors(
new TransactionLogInterceptor({
microservice: 'my-service',
operation: 'api-request',
elasticLogger,
})
)
export class ApiController {
@Get('users')
async getUsers() {
return [{ id: 1, name: 'JoΓ£o' }]
}
}import { Controller, Get, UseInterceptors } from '@nestjs/common'
import {
AutoTransactionLogInterceptor,
TransactionLog,
} from '@starbemtech/star-node-stack-helper'
@Controller('api')
@UseInterceptors(
new AutoTransactionLogInterceptor({
elasticLogger,
defaultMicroservice: 'my-service',
})
)
export class ApiController {
@Get('users')
@TransactionLog({
operation: 'get-users',
microservice: 'user-service',
})
async getUsers() {
return [{ id: 1, name: 'JoΓ£o' }]
}
}import { Controller, Get, UseGuards } from '@nestjs/common'
import { LogGuard } from '@starbemtech/star-node-stack-helper'
@Controller('protected')
@UseGuards(
new LogGuard({
elasticLogger,
logLevel: 'info',
})
)
export class ProtectedController {
@Get('data')
async getProtectedData() {
return { data: 'sensitive information' }
}
}import { Module } from '@nestjs/common'
import { APP_FILTER } from '@nestjs/core'
import { LogExceptionFilter } from '@starbemtech/star-node-stack-helper'
@Module({
providers: [
{
provide: APP_FILTER,
useClass: LogExceptionFilter,
},
],
})
export class AppModule {}import {
Module,
Controller,
Get,
Post,
Body,
UseInterceptors,
} from '@nestjs/common'
import { APP_INTERCEPTOR, APP_FILTER } from '@nestjs/core'
import {
ElasticLogger,
LoggerConfig,
TransactionLogInterceptor,
LogExceptionFilter,
TransactionLog,
Log,
} from '@starbemtech/star-node-stack-helper'
// ConfiguraΓ§Γ£o
const elasticConfig: LoggerConfig = {
node: 'https://your-opensearch-endpoint.com',
service: 'my-nestjs-app',
environment: 'production',
index: 'nestjs-logs',
region: 'us-east-1',
authType: 'aws',
}
const elasticLogger = new ElasticLogger(elasticConfig)
// Controller
@Controller('api')
@UseInterceptors(
new TransactionLogInterceptor({
microservice: 'api-service',
operation: 'api-request',
elasticLogger,
})
)
export class ApiController {
@Get('users')
@Log('info', 'Fetching users list')
async getUsers() {
return [
{ id: 1, name: 'JoΓ£o' },
{ id: 2, name: 'Maria' },
]
}
@Post('users')
@TransactionLog({
microservice: 'user-service',
operation: 'create-user',
elasticLogger,
})
async createUser(@Body() userData: any) {
return {
id: Math.floor(Math.random() * 1000),
...userData,
createdAt: new Date().toISOString(),
}
}
}
// MΓ³dulo Principal
@Module({
controllers: [ApiController],
providers: [
{
provide: APP_INTERCEPTOR,
useValue: new TransactionLogInterceptor({
microservice: 'my-nestjs-app',
operation: 'global-request',
elasticLogger,
}),
},
{
provide: APP_FILTER,
useClass: LogExceptionFilter,
},
],
})
export class AppModule {}import { TransactionLogInterceptor } from '@starbemtech/star-node-stack-helper'
const customInterceptor = new TransactionLogInterceptor({
microservice: 'my-service',
operation: 'api-request',
elasticLogger,
customContext: (context) => ({
userId: context.switchToHttp().getRequest().user?.id,
sessionId: context.switchToHttp().getRequest().sessionId,
customField: 'custom-value',
}),
})import { LogExceptionFilter } from '@starbemtech/star-node-stack-helper'
const customExceptionFilter = new LogExceptionFilter({
elasticLogger,
logLevel: 'error',
customContext: (host) => ({
requestId: host.switchToHttp().getRequest().id,
userId: host.switchToHttp().getRequest().user?.id,
}),
})import { performanceLoggerMiddleware } from '@starbemtech/star-node-stack-helper'
// Apply to specific routes
app.get(
'/api/slow-operation',
performanceLoggerMiddleware('my-service', 'slow-operation', 'production'),
(req, res) => {
// Middleware automatically measures and logs performance
setTimeout(() => {
res.json({ message: 'Operation completed' })
}, 1000)
}
)
// Apply to multiple routes
const performanceMiddleware = performanceLoggerMiddleware(
'my-service',
'api-call',
'production'
)
app.use('/api', performanceMiddleware)import {
transactionLoggerMiddleware,
ElasticLogger,
} from '@starbemtech/star-node-stack-helper'
const elasticLogger = new ElasticLogger(loggerConfig)
// Apply to specific routes
app.post(
'/api/users',
transactionLoggerMiddleware('user-service', 'create-user', elasticLogger),
(req, res) => {
// Middleware automatically captures (agora via evento 'finish'):
// - Request metadata (method, path, baseUrl, route, host, ip/x-forwarded-for, userAgent, httpVersion)
// - Context enriquecido com params/query/body sanitizados (campos sensΓveis mascarados) e com limite de tamanho + hash
// - Headers relevantes (x-user-id, x-platform, authorization [mascarado], content-type, etc.)
// - Status/timing e responseSize (quando disponΓvel via content-length)
// - GeraΓ§Γ£o/propagaΓ§Γ£o de transactionId
const user = createUser(req.body)
res.status(201).json({
user,
transactionId: req.transactionId,
})
}
)
// Apply globally with different operations
app.use('/api', (req, res, next) => {
const operation = `${req.method.toLowerCase()}-${req.path.split('/').pop()}`
return transactionLoggerMiddleware('api-service', operation, elasticLogger)(
req,
res,
next
)
})- O middleware usa
res.once('finish')para garantir logging mesmo quando a resposta Γ© enviada porres.endou streams. - O objeto
contextincluiparams,queryebodysanitizados de forma recursiva (campos sensΓveis comopassword,token,secret, etc. sΓ£o mascarados) e serializaΓ§Γ£o limitada para evitar logs gigantes. - Para propagar o identificador para clientes, combine com o middleware
addTransactionId, que adiciona o headerX-Transaction-IDΓ resposta.
import { addTransactionId } from '@starbemtech/star-node-stack-helper'
// Add transaction ID to all requests
app.use(addTransactionId)
app.get('/api/data', (req, res) => {
// req.transactionId is now available
res.json({
data: 'some data',
transactionId: req.transactionId,
})
})import {
SecretConfig,
RetryConfig,
LoadSecretsOptions,
} from '@starbemtech/star-node-stack-helper'
interface SecretConfig {
region: string
secretName: string | string[]
retry?: RetryConfig
credentials?: {
accessKeyId: string
secretAccessKey: string
}
}
interface RetryConfig {
maxRetries: number
retryDelay: number
exponentialBackoff: boolean
}import {
LoggerConfig,
LogLevel,
LogTransaction,
} from '@starbemtech/star-node-stack-helper'
interface LoggerConfig {
node: string
service: string
environment: string
index: string
region: string
authType: 'aws' | 'basic'
username?: string
password?: string
}
interface LogTransaction {
name: string
microservice: string
operation: string
status: 'success' | 'fail'
duration: number
context?: Record<string, unknown>
requestMeta?: {
method: string
path: string
ip?: string
userAgent?: string
}
}import { PinoLoggerConfig } from '@starbemtech/star-node-stack-helper'
interface PinoLoggerConfig {
serviceName: string
environment: string
logLevel: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'
customFormatters?: {
level?: (label: string) => unknown
log?: (object: unknown) => unknown
}
customSerializers?: {
req?: (req: unknown) => unknown
res?: (res: unknown) => unknown
err?: (err: unknown) => unknown
}
redactPaths?: string[]
}import {
SlackConfig,
SlackMessage,
SlackResponse,
SlackNotificationOptions,
SlackWebhookConfig,
SlackWebhookMessage,
SlackBlock,
SlackAttachment,
} from '@starbemtech/star-node-stack-helper'
interface SlackConfig {
token: string
defaultChannel?: string
botName?: string
baseUrl?: string
}
interface SlackMessage {
channel: string
text: string
blocks?: SlackBlock[]
attachments?: SlackAttachment[]
username?: string
icon_emoji?: string
icon_url?: string
thread_ts?: string
replace_original?: boolean
delete_original?: boolean
}
interface SlackResponse {
ok: boolean
channel?: string
ts?: string
message?: {
text: string
user: string
ts: string
type: string
subtype?: string
}
error?: string
details?: string
}import express from 'express'
import {
ElasticLogger,
LoggerConfig,
loadSecrets,
transactionLoggerMiddleware,
createPinoLogger,
createHttpLogger,
performanceLoggerMiddleware,
pinoLogContext,
} from '@starbemtech/star-node-stack-helper'
// Load secrets on startup
await loadSecrets({
region: 'us-east-1',
secretName: ['prod/database/credentials', 'prod/api/keys'],
})
// Configure loggers
const elasticConfig: LoggerConfig = {
node: process.env.OPENSEARCH_ENDPOINT!,
service: 'my-api',
environment: 'production',
index: 'api-logs',
region: 'us-east-1',
authType: 'aws',
}
const elasticLogger = new ElasticLogger(elasticConfig)
const pinoLogger = createPinoLogger({
serviceName: 'my-api',
environment: 'production',
logLevel: 'info',
})
const app = express()
// Global middleware
app.use(createHttpLogger(pinoLogger))
app.use(express.json())
// Performance monitoring for all API routes
app.use(
'/api',
performanceLoggerMiddleware('my-api', 'api-request', 'production')
)
// Transaction logging for specific operations
app.post(
'/api/users',
transactionLoggerMiddleware('user-service', 'create-user', elasticLogger),
async (req, res) => {
try {
const user = await createUser(req.body)
pinoLogger.info('User created successfully', {
...pinoLogContext.request(req, { userId: user.id }),
action: 'user_creation',
userRole: user.role,
})
res.status(201).json({
user,
transactionId: req.transactionId,
})
} catch (error) {
pinoLogger.error('Failed to create user', {
...pinoLogContext.request(req),
...pinoLogContext.error(error),
action: 'user_creation',
})
res.status(500).json({
error: 'Internal server error',
transactionId: req.transactionId,
})
}
}
)
app.listen(3000, () => {
pinoLogger.info('Server started', {
port: 3000,
environment: 'production',
})
})import { APIGatewayProxyHandler } from 'aws-lambda'
import {
ElasticLogger,
loadSecrets,
pinoLogContext,
} from '@starbemtech/star-node-stack-helper'
// Load secrets once (Lambda container reuse)
let secretsLoaded = false
let logger: ElasticLogger
export const handler: APIGatewayProxyHandler = async (event) => {
// Load secrets only once per container
if (!secretsLoaded) {
await loadSecrets({
region: process.env.AWS_REGION!,
secretName: ['prod/api/keys'],
})
logger = new ElasticLogger({
node: process.env.OPENSEARCH_ENDPOINT!,
service: 'lambda-api',
environment: 'production',
index: 'lambda-logs',
region: process.env.AWS_REGION!,
authType: 'aws',
})
secretsLoaded = true
}
try {
// Your business logic here
const result = await processRequest(event)
// Log successful operation
await logger.logTransaction({
name: 'api-request',
microservice: 'lambda-api',
operation: 'process-request',
status: 'success',
duration: Date.now() - event.requestContext.requestTime,
context: {
path: event.path,
method: event.httpMethod,
userId: event.requestContext.authorizer?.userId,
},
requestMeta: {
method: event.httpMethod,
path: event.path,
ip: event.requestContext.identity.sourceIp,
userAgent: event.headers['User-Agent'],
},
})
return {
statusCode: 200,
body: JSON.stringify(result),
}
} catch (error) {
// Log error
await logger.log('error', 'Lambda function error', {
...pinoLogContext.error(error),
path: event.path,
method: event.httpMethod,
})
return {
statusCode: 500,
body: JSON.stringify({ error: 'Internal server error' }),
}
}
}Loads secrets from AWS Secrets Manager and sets them as environment variables.
Parameters:
config: Configuration object with region, secret names, and optional retry settings
Example:
await loadSecrets({
region: 'us-east-1',
secretName: ['prod/database/credentials'],
})Tests if secrets were loaded correctly by logging environment variables.
Returns true if the application is running on AWS (Lambda, EC2, etc.).
Returns the current AWS region or undefined if not running on AWS.
Creates a new ElasticLogger instance.
Logs a message with the specified level and metadata.
Logs a transaction with detailed metadata.
Searches system logs with the given query.
Searches transaction logs with the given query.
Checks the health of the OpenSearch cluster.
Tests the connection to OpenSearch.
Flushes all indices.
Recreates the index (useful for schema changes).
Sends a message to Slack using the official API.
Parameters:
message: The message to send with channel, text, and optional formattingoptions: Configuration including token, retry settings, error handling, and channel membership check
Options:
config: Slack configuration with token and default settingsretryConfig: Retry configuration for failed requestsfailSilently: Whether to throw errors or return error responsescheckChannelMembership: Whether to verify bot is in channel before sending (default: false)
Example:
const response = await sendSlackMessage(
{
channel: '#general',
text: 'Hello from my app!',
username: 'My Bot',
},
{
config: {
token: 'xoxb-your-bot-token',
defaultChannel: '#general',
},
failSilently: true,
}
)sendSlackWebhook(message: SlackWebhookMessage, config: SlackWebhookConfig, options?: object): Promise<SlackResponse>
Sends a message to Slack using a webhook.
Parameters:
message: The message to sendconfig: Webhook configuration with URL and default settingsoptions: Optional retry and error handling settings
Creates a section block for formatted messages. Now includes automatic text sanitization and validation.
Creates a divider block for visual separation. Recommended for replacing empty section blocks.
Creates a button element for interactive messages.
Creates an actions block with buttons.
Creates an attachment for rich message formatting.
Creates a SlackNotifier instance for convenient message management.
Methods:
sendMessage(text: string, channel?: string): Promise<SlackResponse>sendSuccess(message: string, channel?: string): Promise<SlackResponse>sendError(error: string, channel?: string): Promise<SlackResponse>sendWarning(warning: string, channel?: string): Promise<SlackResponse>sendFormattedMessage(blocks: SlackBlock[], channel?: string, fallbackText?: string): Promise<SlackResponse>
Creates a configured Pino logger instance.
Creates an Express HTTP logging middleware.
pinoLogContext.request(req: Request, additionalData?: Record<string, unknown>): Record<string, unknown>
Creates request context for logging.
Creates error context for logging.
pinoLogContext.performance(operation: string, duration: number, metadata?: Record<string, unknown>): Record<string, unknown>
Creates performance context for logging.
performanceLoggerMiddleware(service: string, operation: string, environment: string): ExpressMiddleware
Creates middleware that measures and logs request performance.
transactionLoggerMiddleware(service: string, operation: string, elasticLogger: ElasticLogger | null): ExpressMiddleware
Creates middleware that logs detailed transaction information.
Adds a transaction ID to the request object.
Decorator for basic logging with specified level and message.
Decorator for performance logging that automatically measures execution time.
Decorator for error logging with specified message.
Decorator for critical error logging.
Decorator for transaction logging with custom options.
Manual transaction logging interceptor for NestJS applications.
Constructor Options:
microservice: string- Name of the microserviceoperation: string- Operation being performedelasticLogger: ElasticLogger- ElasticLogger instancecustomContext?: (context: ExecutionContext) => Record<string, unknown>- Custom context function
Automatic transaction logging interceptor that reads metadata from decorators.
Constructor Options:
elasticLogger: ElasticLogger- ElasticLogger instancedefaultMicroservice?: string- Default microservice namecustomContext?: (context: ExecutionContext) => Record<string, unknown>- Custom context function
Guard for authentication/authorization with automatic logging.
Constructor Options:
elasticLogger: ElasticLogger- ElasticLogger instancelogLevel?: LogLevel- Log level for guard operationscustomContext?: (context: ExecutionContext) => Record<string, unknown>- Custom context function
Exception filter that automatically logs errors to Elasticsearch/OpenSearch.
Constructor Options:
elasticLogger: ElasticLogger- ElasticLogger instancelogLevel?: LogLevel- Log level for exceptionscustomContext?: (host: ArgumentsHost) => Record<string, unknown>- Custom context function
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
For support, email support@starbem.app or create an issue in the GitHub repository.
Creates a configured Pino logger instance with optimized settings for development and production.
Parameters:
config.serviceName: Name of the serviceconfig.environment: Environment ('development' | 'production')config.logLevel: Log level ('trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal')
Creates an Express HTTP logging middleware with optimized settings.
Features:
- Automatic silent routes for health checks and metrics
- Smart log levels based on status codes
- Request/response serialization
- Performance tracking
Creates request context for logging.
Creates error context for logging.
Creates performance context for logging.
Creates proxy/API Gateway context for logging.