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

@develop-x/nest-consul

v1.0.3

Published

Nest Consul

Readme

@develop-x/nest-consul

Overview

@develop-x/nest-consul is a NestJS package that provides Consul integration for service discovery and key-value store operations. It enables microservices to register themselves, discover other services, and manage configuration data through Consul's distributed system.

Installation

npm install @develop-x/nest-consul

Features

  • Service Registration: Automatic service registration with Consul
  • Service Discovery: Find and connect to other services
  • Health Checks: Built-in health check endpoints
  • Key-Value Store: Manage configuration and shared data
  • Load Balancing: Automatic load balancing across service instances
  • Configuration Management: Centralized configuration storage
  • TypeScript Support: Full TypeScript support with proper typing

Usage

Module Import

Import the ConsulModule in your application module:

import { Module } from '@nestjs/common';
import { ConsulModule } from '@develop-x/nest-consul';

@Module({
  imports: [
    ConsulModule.forRoot('http://consul:8500'), // Consul server URL
  ],
})
export class AppModule {}

Service Registration

Register your service with Consul:

import { Injectable, OnModuleInit } from '@nestjs/common';
import { ConsulService } from '@develop-x/nest-consul';

@Injectable()
export class AppService implements OnModuleInit {
  constructor(private readonly consulService: ConsulService) {}

  async onModuleInit() {
    await this.consulService.registerService({
      name: 'user-service',
      id: 'user-service-1',
      address: 'localhost',
      port: 3000,
      tags: ['api', 'users', 'v1'],
      check: {
        http: 'http://localhost:3000/health',
        interval: '10s',
        timeout: '5s',
      },
    });
  }
}

Service Discovery

Discover and connect to other services:

import { Injectable } from '@nestjs/common';
import { ConsulService } from '@develop-x/nest-consul';

@Injectable()
export class UserService {
  constructor(private readonly consulService: ConsulService) {}

  async getOrderServiceUrl(): Promise<string> {
    const services = await this.consulService.getHealthyServices('order-service');
    
    if (services.length === 0) {
      throw new Error('Order service not available');
    }

    // Simple round-robin load balancing
    const service = services[Math.floor(Math.random() * services.length)];
    return `http://${service.address}:${service.port}`;
  }

  async callOrderService(userId: string) {
    const orderServiceUrl = await this.getOrderServiceUrl();
    
    // Make HTTP request to order service
    const response = await fetch(`${orderServiceUrl}/orders?userId=${userId}`);
    return response.json();
  }
}

API Reference

ConsulModule Configuration

forRoot(consulUrl: string)

Configure the Consul module with the Consul server URL.

Parameters:

  • consulUrl: string - Consul server URL (e.g., 'http://localhost:8500')

ConsulService

registerService(serviceConfig: ServiceConfig)

Register a service with Consul.

Parameters:

interface ServiceConfig {
  name: string;           // Service name
  id: string;            // Unique service instance ID
  address: string;       // Service address/hostname
  port: number;          // Service port
  tags?: string[];       // Service tags for filtering
  meta?: Record<string, string>; // Service metadata
  check?: HealthCheck;   // Health check configuration
}

interface HealthCheck {
  http?: string;         // HTTP health check URL
  tcp?: string;          // TCP health check address:port
  interval: string;      // Check interval (e.g., '10s')
  timeout: string;       // Check timeout (e.g., '5s')
  deregisterCriticalServiceAfter?: string; // Auto-deregister after
}

getHealthyServices(serviceName: string)

Get all healthy instances of a service.

Parameters:

  • serviceName: string - Name of the service to discover

Returns: Promise<ServiceInstance[]>

interface ServiceInstance {
  id: string;
  name: string;
  address: string;
  port: number;
  tags: string[];
  meta: Record<string, string>;
}

deregisterService(serviceId: string)

Deregister a service from Consul.

Parameters:

  • serviceId: string - Unique service instance ID

KvStoreService

set(key: string, value: any)

Store a value in Consul's key-value store.

await this.kvStoreService.set('config/database/url', 'postgresql://...');
await this.kvStoreService.set('config/features', { featureA: true, featureB: false });

get(key: string)

Retrieve a value from Consul's key-value store.

const dbUrl = await this.kvStoreService.get<string>('config/database/url');
const features = await this.kvStoreService.get<FeatureFlags>('config/features');

delete(key: string)

Delete a key from Consul's key-value store.

await this.kvStoreService.delete('config/deprecated-setting');

getKeys(prefix: string)

Get all keys with a specific prefix.

const configKeys = await this.kvStoreService.getKeys('config/');

Advanced Usage

Health Check Endpoint

Create a health check endpoint for your service:

import { Controller, Get } from '@nestjs/common';

@Controller('health')
export class HealthController {
  @Get()
  getHealth() {
    return {
      status: 'ok',
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
    };
  }
}

Service Discovery with Load Balancing

Implement advanced load balancing strategies:

import { Injectable } from '@nestjs/common';
import { ConsulService } from '@develop-x/nest-consul';

@Injectable()
export class LoadBalancedService {
  private serviceCounters = new Map<string, number>();

  constructor(private readonly consulService: ConsulService) {}

  async getServiceUrl(serviceName: string, strategy: 'round-robin' | 'random' = 'round-robin'): Promise<string> {
    const services = await this.consulService.getHealthyServices(serviceName);
    
    if (services.length === 0) {
      throw new Error(`No healthy instances of ${serviceName} found`);
    }

    let selectedService;

    switch (strategy) {
      case 'round-robin':
        const counter = this.serviceCounters.get(serviceName) || 0;
        selectedService = services[counter % services.length];
        this.serviceCounters.set(serviceName, counter + 1);
        break;
      
      case 'random':
      default:
        selectedService = services[Math.floor(Math.random() * services.length)];
        break;
    }

    return `http://${selectedService.address}:${selectedService.port}`;
  }
}

Configuration Management

Use Consul KV store for centralized configuration:

import { Injectable, OnModuleInit } from '@nestjs/common';
import { KvStoreService } from '@develop-x/nest-consul';

@Injectable()
export class ConfigService implements OnModuleInit {
  private config: any = {};

  constructor(private readonly kvStore: KvStoreService) {}

  async onModuleInit() {
    await this.loadConfiguration();
  }

  private async loadConfiguration() {
    try {
      // Load database configuration
      this.config.database = await this.kvStore.get('config/database');
      
      // Load feature flags
      this.config.features = await this.kvStore.get('config/features');
      
      // Load API keys
      this.config.apiKeys = await this.kvStore.get('config/api-keys');
      
      console.log('Configuration loaded from Consul');
    } catch (error) {
      console.error('Failed to load configuration from Consul:', error);
      // Use default configuration or environment variables as fallback
    }
  }

  get<T>(key: string): T {
    return this.getNestedValue(this.config, key);
  }

  private getNestedValue(obj: any, path: string): any {
    return path.split('.').reduce((current, key) => current?.[key], obj);
  }

  async updateConfig(key: string, value: any) {
    await this.kvStore.set(`config/${key}`, value);
    await this.loadConfiguration(); // Reload configuration
  }
}

Service Mesh Integration

Integrate with service mesh patterns:

import { Injectable } from '@nestjs/common';
import { ConsulService } from '@develop-x/nest-consul';

@Injectable()
export class ServiceMeshService {
  constructor(private readonly consulService: ConsulService) {}

  async registerWithMesh(serviceConfig: {
    name: string;
    version: string;
    protocol: 'http' | 'grpc';
    endpoints: string[];
  }) {
    await this.consulService.registerService({
      name: serviceConfig.name,
      id: `${serviceConfig.name}-${serviceConfig.version}-${Date.now()}`,
      address: process.env.SERVICE_HOST || 'localhost',
      port: parseInt(process.env.SERVICE_PORT || '3000'),
      tags: [
        `version:${serviceConfig.version}`,
        `protocol:${serviceConfig.protocol}`,
        ...serviceConfig.endpoints.map(ep => `endpoint:${ep}`),
      ],
      meta: {
        version: serviceConfig.version,
        protocol: serviceConfig.protocol,
        endpoints: serviceConfig.endpoints.join(','),
      },
      check: {
        http: `http://${process.env.SERVICE_HOST || 'localhost'}:${process.env.SERVICE_PORT || '3000'}/health`,
        interval: '10s',
        timeout: '5s',
        deregisterCriticalServiceAfter: '30s',
      },
    });
  }

  async discoverServicesByTag(tag: string) {
    const allServices = await this.consulService.getHealthyServices('');
    return allServices.filter(service => service.tags.includes(tag));
  }
}

Multi-Environment Configuration

Handle different environments with Consul:

import { Injectable } from '@nestjs/common';
import { KvStoreService } from '@develop-x/nest-consul';

@Injectable()
export class EnvironmentConfigService {
  private environment = process.env.NODE_ENV || 'development';

  constructor(private readonly kvStore: KvStoreService) {}

  async getEnvironmentConfig<T>(configKey: string): Promise<T> {
    const envKey = `config/${this.environment}/${configKey}`;
    
    try {
      return await this.kvStore.get<T>(envKey);
    } catch (error) {
      // Fallback to default configuration
      const defaultKey = `config/default/${configKey}`;
      return await this.kvStore.get<T>(defaultKey);
    }
  }

  async setEnvironmentConfig(configKey: string, value: any) {
    const envKey = `config/${this.environment}/${configKey}`;
    await this.kvStore.set(envKey, value);
  }
}

Integration Examples

With Docker and Docker Compose

# docker-compose.yml
version: '3.8'
services:
  consul:
    image: consul:latest
    ports:
      - "8500:8500"
    command: consul agent -dev -client=0.0.0.0

  user-service:
    build: ./user-service
    environment:
      - CONSUL_URL=http://consul:8500
      - SERVICE_NAME=user-service
      - SERVICE_PORT=3000
    depends_on:
      - consul
    ports:
      - "3000:3000"

  order-service:
    build: ./order-service
    environment:
      - CONSUL_URL=http://consul:8500
      - SERVICE_NAME=order-service
      - SERVICE_PORT=3001
    depends_on:
      - consul
    ports:
      - "3001:3001"

With Kubernetes

# consul-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: consul
spec:
  selector:
    app: consul
  ports:
    - port: 8500
      targetPort: 8500
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: consul
spec:
  replicas: 1
  selector:
    matchLabels:
      app: consul
  template:
    metadata:
      labels:
        app: consul
    spec:
      containers:
      - name: consul
        image: consul:latest
        args: ["consul", "agent", "-dev", "-client=0.0.0.0"]
        ports:
        - containerPort: 8500

Graceful Shutdown

Handle graceful service deregistration:

import { Injectable, OnApplicationShutdown } from '@nestjs/common';
import { ConsulService } from '@develop-x/nest-consul';

@Injectable()
export class AppService implements OnApplicationShutdown {
  private serviceId: string;

  constructor(private readonly consulService: ConsulService) {}

  async onModuleInit() {
    this.serviceId = `user-service-${Date.now()}`;
    
    await this.consulService.registerService({
      name: 'user-service',
      id: this.serviceId,
      address: 'localhost',
      port: 3000,
      check: {
        http: 'http://localhost:3000/health',
        interval: '10s',
        timeout: '5s',
      },
    });
  }

  async onApplicationShutdown(signal?: string) {
    console.log(`Received shutdown signal: ${signal}`);
    
    try {
      await this.consulService.deregisterService(this.serviceId);
      console.log('Service deregistered from Consul');
    } catch (error) {
      console.error('Failed to deregister service:', error);
    }
  }
}

Testing

Unit Testing

import { Test, TestingModule } from '@nestjs/testing';
import { ConsulService } from '@develop-x/nest-consul';
import { UserService } from './user.service';

describe('UserService', () => {
  let service: UserService;
  let consulService: ConsulService;

  beforeEach(async () => {
    const mockConsulService = {
      getHealthyServices: jest.fn(),
      registerService: jest.fn(),
      deregisterService: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: ConsulService,
          useValue: mockConsulService,
        },
      ],
    }).compile();

    service = module.get<UserService>(UserService);
    consulService = module.get<ConsulService>(ConsulService);
  });

  it('should get order service URL', async () => {
    const mockServices = [
      { address: 'localhost', port: 3001, id: 'order-1', name: 'order-service', tags: [], meta: {} },
    ];
    
    (consulService.getHealthyServices as jest.Mock).mockResolvedValue(mockServices);

    const url = await service.getOrderServiceUrl();
    
    expect(url).toBe('http://localhost:3001');
    expect(consulService.getHealthyServices).toHaveBeenCalledWith('order-service');
  });
});

Integration Testing

import { Test, TestingModule } from '@nestjs/testing';
import { ConsulModule } from '@develop-x/nest-consul';
import { UserService } from './user.service';

describe('UserService Integration', () => {
  let service: UserService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [
        ConsulModule.forRoot('http://localhost:8500'), // Test Consul instance
      ],
      providers: [UserService],
    }).compile();

    service = module.get<UserService>(UserService);
  });

  it('should discover services from Consul', async () => {
    // This test requires a running Consul instance
    const url = await service.getOrderServiceUrl();
    expect(url).toMatch(/^http:\/\/.+:\d+$/);
  });
});

Best Practices

  1. Service Naming: Use consistent and descriptive service names
  2. Health Checks: Always implement proper health check endpoints
  3. Graceful Shutdown: Properly deregister services on shutdown
  4. Error Handling: Handle service discovery failures gracefully
  5. Load Balancing: Implement appropriate load balancing strategies
  6. Configuration: Use Consul KV store for centralized configuration
  7. Monitoring: Monitor service health and registration status
  8. Security: Secure Consul access in production environments

Security Considerations

  1. ACL Tokens: Use Consul ACL tokens for authentication
  2. TLS Encryption: Enable TLS for Consul communication
  3. Network Security: Restrict network access to Consul
  4. Sensitive Data: Encrypt sensitive configuration data

Troubleshooting

Common Issues

  1. Service Not Found: Check service registration and health status
  2. Connection Refused: Verify Consul server is running and accessible
  3. Health Check Failures: Ensure health check endpoints are working
  4. Load Balancing Issues: Verify multiple service instances are registered

Debug Mode

Enable debug logging for troubleshooting:

// Set environment variable
process.env.DEBUG = 'consul:*';

// Or use console logging
console.log('Consul services:', await consulService.getHealthyServices('user-service'));

Dependencies

  • @nestjs/common: NestJS common utilities
  • @nestjs/core: NestJS core functionality

License

ISC

Support

For issues and questions, please refer to the project repository or contact the development team.