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

@inh-lib/api-util-fastify

v1.3.14

Published

> Fastify plugin for unified telemetry integration with OpenTelemetry

Readme

@inh-lib/api-util-fastify - Telemetry Plugin

Fastify plugin for unified telemetry integration with OpenTelemetry

A comprehensive Fastify plugin that seamlessly integrates with @inh-lib/unified-telemetry-otel to provide automatic telemetry (tracing, logging, metrics) for your Fastify applications while converting Fastify context to UnifiedHttpContext.

✨ Features

  • 🔧 Fastify Plugin - Easy registration with your Fastify application
  • 📊 Automatic Telemetry - Auto-tracing, logging, and metrics for HTTP requests
  • 🔄 Context Conversion - Seamless conversion from Fastify to UnifiedHttpContext
  • 🎯 Enhanced Context - Direct access to telemetry provider in context
  • 🚀 Type Safe - Full TypeScript support with comprehensive types
  • Zero Config - Works out of the box with sensible defaults
  • 🛡️ Error Handling - Automatic error tracking and span management

📦 Installation

npm install @inh-lib/api-util-fastify @inh-lib/unified-telemetry-otel @inh-lib/unified-route

🚀 Quick Start

Basic Setup

import Fastify from 'fastify';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OtelProviderService } from '@inh-lib/unified-telemetry-otel';
import { TelemetryPluginService } from '@inh-lib/api-util-fastify';

// Setup OpenTelemetry
const sdk = new NodeSDK({
  serviceName: 'my-fastify-service',
  serviceVersion: '1.0.0'
});
sdk.start();

// Create telemetry provider
const telemetryProvider = OtelProviderService.createProviderWithConsole({
  config: {
    serviceName: 'my-fastify-service',
    serviceVersion: '1.0.0',
    environment: 'development'
  }
}, sdk);

// Create Fastify instance
const fastify = Fastify({ logger: true });

// Register telemetry plugin
await fastify.register(TelemetryPluginService.createPlugin({
  telemetryProvider,
  autoTracing: true,
  serviceName: 'my-fastify-service',
  skipRoutes: ['/health', '/metrics']
}));

// Routes automatically get telemetry
fastify.get('/users/:id', async (request, reply) => {
  const context = fastify.telemetry.createEnhancedContext(request, reply);
  
  // Use telemetry directly from context
  const logger = context.telemetry.logger.getBaseLogger();
  logger.info('Getting user', { userId: request.params.id });
  
  return { user: { id: request.params.id } };
});

await fastify.listen({ port: 3000 });

📚 API Reference

TelemetryPluginService

createPlugin(options: TelemetryPluginOptions)

Creates a Fastify plugin with telemetry integration.

Options:

  • telemetryProvider: UnifiedTelemetryProvider - The telemetry provider instance
  • autoTracing?: boolean - Enable automatic HTTP request tracing (default: true)
  • serviceName?: string - Service name for telemetry (default: 'fastify-service')
  • skipRoutes?: string[] - Routes to skip telemetry (default: ['/health', '/metrics', '/ping'])
import { TelemetryPluginOptions } from '@inh-lib/api-util-fastify/src/telemetry.types';

const options: TelemetryPluginOptions = {
  telemetryProvider,
  autoTracing: true,
  serviceName: 'my-service',
  skipRoutes: ['/health', '/ping']
};

const plugin = TelemetryPluginService.createPlugin(options);
await fastify.register(plugin);

createEnhancedContext<TBody>(request, reply, provider)

Creates an enhanced UnifiedHttpContext with direct telemetry access.

const context = TelemetryPluginService.createEnhancedContext<CreateUserRequest>(
  request, 
  reply, 
  telemetryProvider
);

// Direct access to telemetry
context.telemetry.logger.getBaseLogger().info('Message');
context.telemetry.tracer.startSpan('operation');
context.telemetry.metrics.createCounter('requests_total');

createContextWithTelemetry<TBody>(request, reply, provider)

Creates a regular UnifiedHttpContext with telemetry provider in registry.

const context = TelemetryPluginService.createContextWithTelemetry<UserData>(
  request, 
  reply, 
  telemetryProvider
);

// Access telemetry from registry
const provider = getRegistryItem(context, TELEMETRY_REGISTRY_KEYS.TELEMETRY_PROVIDER);

Fastify Decorator

When the plugin is registered, it adds a telemetry decorator to the Fastify instance:

declare module 'fastify' {
  interface FastifyInstance {
    telemetry: {
      provider: UnifiedTelemetryProvider;
      createEnhancedContext: <TBody>(req, res) => EnhancedUnifiedHttpContext;
      createContext: <TBody>(req, res) => UnifiedHttpContext;
    };
  }
}

// Usage
const context = fastify.telemetry.createEnhancedContext(request, reply);

🔧 Advanced Usage

With Unified Route Middleware

import { 
  UnifiedMiddleware, 
  composeMiddleware,
  sendResponse,
  sendError,
  addRegistryItem,
  getRegistryItem 
} from '@inh-lib/unified-route';

// Authentication middleware
const authMiddleware: UnifiedMiddleware = async (context, next) => {
  const token = context.request.headers.authorization;
  
  if (!token) {
    return sendError(context, 'Authorization required', 401);
  }
  
  // Use telemetry from enhanced context
  if ('telemetry' in context) {
    const logger = context.telemetry.logger.getBaseLogger();
    logger.info('Authenticating user', { token: token.substring(0, 10) + '...' });
  }
  
  const user = await authenticateUser(token);
  addRegistryItem(context, 'user', user);
  await next();
};

// Route with middleware composition
fastify.get<{ Params: { id: string } }>('/users/:id', async (request, reply) => {
  const context = fastify.telemetry.createEnhancedContext(request, reply);
  
  const handler = composeMiddleware([authMiddleware])(async (ctx) => {
    const user = getRegistryItem(ctx, 'user');
    const userId = ctx.request.params.id;
    
    sendResponse(ctx, { user, requestedId: userId });
  });
  
  await handler(context);
});

Custom Telemetry in Routes

interface CreateOrderRequest {
  items: Array<{ id: string; quantity: number }>;
  customerId: string;
}

fastify.post<{ Body: CreateOrderRequest }>('/orders', async (request, reply) => {
  const context = fastify.telemetry.createEnhancedContext<CreateOrderRequest>(request, reply);
  const { items, customerId } = context.request.body;
  
  // Create custom span
  const span = context.telemetry.tracer.startSpan('order.create', {
    kind: 'internal',
    attributes: {
      'order.items_count': items.length,
      'order.customer_id': customerId
    }
  });
  
  // Create contextual logger
  const logger = context.telemetry.logger.getLogger({
    span,
    options: {
      operationType: 'business',
      operationName: 'createOrder',
      layer: 'service',
      autoAddSpanEvents: true
    }
  });
  
  try {
    logger.info('Creating order', { customerId, itemCount: items.length });
    
    // Business logic here
    const order = await createOrder({ items, customerId });
    
    // Record metrics
    const orderCounter = context.telemetry.metrics.createCounter(
      'orders_created_total',
      'Total orders created'
    );
    orderCounter.add(1, { customer_type: 'premium' });
    
    logger.info('Order created successfully', { orderId: order.id });
    
    return { order };
  } catch (error) {
    logger.error('Failed to create order', error);
    span.setStatus({ code: 'error', message: error.message });
    throw error;
  } finally {
    logger.finishSpan();
  }
});

Database Integration Example

import { Pool } from 'pg';

const databaseMiddleware: UnifiedMiddleware = async (context, next) => {
  const pool = new Pool({ connectionString: process.env.DATABASE_URL });
  
  // Use telemetry if available
  let logger;
  if ('telemetry' in context) {
    logger = context.telemetry.logger.getBaseLogger();
    logger.info('Database connection established');
  }
  
  const db = {
    query: async (text: string, params?: unknown[]) => {
      const span = 'telemetry' in context 
        ? context.telemetry.tracer.startSpan('db.query', {
            kind: 'client',
            attributes: { 'db.statement': text }
          })
        : null;
      
      try {
        const result = await pool.query(text, params);
        span?.setTag('db.rows_affected', result.rowCount);
        return result.rows;
      } catch (error) {
        span?.recordException(error);
        throw error;
      } finally {
        span?.finish();
      }
    }
  };
  
  addRegistryItem(context, 'db', db);
  
  try {
    await next();
  } finally {
    await pool.end();
    logger?.info('Database connection closed');
  }
};

🏗️ Plugin Architecture

Automatic Features

When autoTracing: true (default), the plugin automatically:

  1. Request Tracing: Creates spans for all HTTP requests
  2. Request Logging: Logs request start and completion
  3. Metrics Collection: Records request count and duration
  4. Error Handling: Captures and logs errors with spans
  5. Request ID: Adds x-request-id header to responses

Hook Integration

The plugin registers Fastify hooks:

  • onRequest: Start span, create logger, add request ID
  • onResponse: Log completion, record metrics, finish span
  • onError: Log errors, record exceptions in span

Registry Keys

import { TELEMETRY_REGISTRY_KEYS } from '@inh-lib/api-util-fastify';

// Available registry keys
TELEMETRY_REGISTRY_KEYS.TELEMETRY_PROVIDER  // 'telemetry:provider'
TELEMETRY_REGISTRY_KEYS.TELEMETRY_SPAN      // 'telemetry:span'
TELEMETRY_REGISTRY_KEYS.TELEMETRY_LOGGER    // 'telemetry:logger'
TELEMETRY_REGISTRY_KEYS.REQUEST_ID          // 'telemetry:request_id'

🧪 Testing

Unit Testing

import { NoOpUnifiedTelemetryProvider } from '@inh-lib/unified-telemetry-core';
import { TelemetryPluginService } from '@inh-lib/api-util-fastify';

describe('API Tests', () => {
  let fastify: FastifyInstance;
  let telemetryProvider: NoOpUnifiedTelemetryProvider;

  beforeEach(async () => {
    fastify = Fastify();
    
    // Use NoOp provider for testing
    telemetryProvider = new NoOpUnifiedTelemetryProvider({
      serviceName: 'test-service',
      serviceVersion: '1.0.0',
      environment: 'test'
    });

    await fastify.register(TelemetryPluginService.createPlugin({
      telemetryProvider,
      autoTracing: false // Disable auto-tracing in tests
    }));
  });

  afterEach(async () => {
    await fastify.close();
    await telemetryProvider.shutdown();
  });

  it('should create enhanced context', async () => {
    fastify.get('/test', async (request, reply) => {
      const context = fastify.telemetry.createEnhancedContext(request, reply);
      expect(context.telemetry).toBeDefined();
      return { success: true };
    });

    const response = await fastify.inject({ method: 'GET', url: '/test' });
    expect(response.statusCode).toBe(200);
  });
});

🔧 Configuration

Environment Variables

# Service configuration
SERVICE_NAME=my-fastify-service
SERVICE_VERSION=1.0.0
ENVIRONMENT=production

# OpenTelemetry configuration
OTEL_SERVICE_NAME=my-fastify-service
OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:14268/api/traces

TypeScript Configuration

{
  "compilerOptions": {
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

📊 Observability Features

Automatic Metrics

The plugin automatically records:

  • http_requests_total - Counter of HTTP requests by method, route, status
  • http_request_duration_ms - Histogram of request durations
  • http_requests_active - Gauge of currently active requests

Automatic Logging

Structured logs include:

  • Request start/end with timing
  • Error logging with stack traces
  • Request IDs for correlation
  • HTTP method, URL, status codes
  • User agent and client IP

Automatic Tracing

Spans include:

  • HTTP method and route as span name
  • Request attributes (method, URL, user agent)
  • Response attributes (status code, duration)
  • Error information and exceptions
  • Proper span status based on HTTP status

🚀 Production Deployment

Docker Example

FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY . .

ENV NODE_ENV=production
ENV SERVICE_NAME=my-fastify-service
ENV OTEL_SERVICE_NAME=my-fastify-service

EXPOSE 3000
CMD ["node", "dist/index.js"]

Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fastify-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: fastify-service
  template:
    metadata:
      labels:
        app: fastify-service
    spec:
      containers:
      - name: app
        image: fastify-service:latest
        ports:
        - containerPort: 3000
        env:
        - name: SERVICE_NAME
          value: "fastify-service"
        - name: OTEL_EXPORTER_OTLP_ENDPOINT
          value: "http://jaeger-collector:14268/api/traces"

🤝 Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Write tests for your changes
  4. Ensure all tests pass (npm test)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

📄 License

MIT License - see LICENSE file for details.

🔗 Related Packages


Made with ❤️ for the Fastify and OpenTelemetry community