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

nomad-client

v0.0.2

Published

TypeScript client library for HashiCorp Nomad API - Complete SDK with type-safe interfaces for jobs, nodes, allocations, and cluster management

Readme

nomad-client

Full HTTP API support for HashiCorp Nomad

🚀 Nomad TypeScript Client

A comprehensive, robust, and type-safe HashiCorp Nomad client for TypeScript/Node.js applications. Built following SOLID principles and modern design patterns, providing an intuitive API to interact with all Nomad resources.

npm version TypeScript License: MIT


✨ Features

  • 🎯 Type-Safe: Complete TypeScript interfaces for the entire Nomad API
  • 🏗️ SOLID Architecture: Extensible, testable, and maintainable codebase
  • 🔐 Advanced Authentication: Multiple auth methods (Token, Bearer, mTLS) with validation
  • 📁 Configuration Loaders: Native support for HCL, JSON, and YAML formats
  • 🔧 Provider Pattern: Unified interface for all Nomad modules
  • 📊 Complete Modules: Jobs, Tasks, Allocations, Namespaces, Nodes, Node Pools
  • 🛡️ Robust Error Handling: Comprehensive error management and validation
  • ⚡ Optimized Performance: High-performance HTTP client with TLS support
  • 📝 Comprehensive Documentation: Practical examples and complete API reference

📦 Installation

Using npm

npm install nomad-client

Using yarn

yarn add nomad-client

Using pnpm

pnpm add nomad-client

🔧 Prerequisites

  • Node.js: 14.x or higher
  • TypeScript: 4.0+ (recommended 4.5+)
  • HashiCorp Nomad: 1.0+ (tested with 1.4+)

🚀 Quick Start

Basic Configuration

import { NomadClient } from 'nomad-client';

// Create provider instance
const nomad = new NomadClient({
    baseUrl: 'http://localhost:4646',
    token: process.env.NOMAD_TOKEN,
    namespace: 'default'
});

// Test connectivity
const isConnected = await nomad.ping();
console.log('Connected:', isConnected);

Authentication Methods

The client supports multiple authentication methods according to HashiCorp Nomad documentation:

Token Authentication (Recommended)

const nomad = new NomadClient({
    baseUrl: 'https://nomad.example.com:4646',
    token: '550e8400-e29b-41d4-a716-446655440000',
    authMethod: 'token' // Uses X-Nomad-Token header (default)
});

Bearer Authentication

const nomad = new NomadClient({
    baseUrl: 'https://nomad.example.com:4646',
    token: '550e8400-e29b-41d4-a716-446655440000',
    authMethod: 'bearer' // Uses Authorization: Bearer header
});

Mutual TLS (mTLS)

const nomad = new NomadClient({
    baseUrl: 'https://nomad.example.com:4646',
    authMethod: 'mtls',
    tls: {
        caCertPath: '/etc/nomad/certs/ca.pem',
        clientCertPath: '/etc/nomad/certs/client.pem',
        clientKeyPath: '/etc/nomad/certs/client.key'
    }
});

Environment Variables

export NOMAD_ADDR="https://nomad.example.com:4646"
export NOMAD_TOKEN="550e8400-e29b-41d4-a716-446655440000"
export NOMAD_NAMESPACE="production"
export NOMAD_REGION="us-west-1"
// Auto-configured from environment variables
const nomad = new NomadClient();

Job Management

// Define job specification
const jobSpec = {
    ID: 'my-web-app',
    Name: 'My Web Application',
    Type: 'service',
    Datacenters: ['dc1'],
    TaskGroups: [{
        Name: 'web',
        Count: 3,
        Tasks: [{
            Name: 'nginx',
            Driver: 'docker',
            Config: {
                image: 'nginx:alpine',
                port_map: { http: 80 }
            },
            Resources: {
                CPU: 100,
                MemoryMB: 128,
                DiskMB: 300
            }
        }]
    }]
};

// Deploy job
const result = await nomad.jobs.create(jobSpec);
console.log('Job created:', result.EvalID);

// Wait for deployment completion
const success = await nomad.deployAndWait(jobSpec, 120000);
console.log('Deployment success:', success);

// Get job status
const status = await nomad.jobs.get('my-web-app');
console.log('Status:', status.Status);

// Scale job
await nomad.jobs.scale('my-web-app', 'web', 5);

// Stop job
await nomad.jobs.stop('my-web-app');

Allocation Monitoring

// List job allocations
const allocations = await nomad.jobs.allocations('my-web-app');

for (const alloc of allocations) {
    console.log(`Allocation: ${alloc.ID}`);
    console.log(`Status: ${alloc.ClientStatus}`);
    
    // Get task logs
    const logs = await nomad.tasks.getLogs(alloc.ID, 'nginx');
    console.log('Logs:', logs.stdout);
    
    // Get resource metrics
    const stats = await nomad.tasks.getStats(alloc.ID, 'nginx');
    console.log(`CPU: ${stats.ResourceUsage.CPU.Percent}%`);
    
    // Health check
    const isHealthy = await nomad.allocations.isHealthy(alloc.ID);
    console.log(`Healthy: ${isHealthy}`);
}

// Stream logs in real-time
const stopStreaming = await nomad.tasks.streamLogs(
    'alloc-id',
    'nginx',
    'stdout',
    (data) => console.log('LIVE LOG:', data)
);

// Stop streaming after 30 seconds
setTimeout(() => stopStreaming(), 30000);

Configuration Loading

import { LoaderFactory } from 'nomad-client';

// Load job from HCL file
const hclLoader = LoaderFactory.getLoader('hcl');
const jobConfig = await hclLoader.load('./my-job.hcl');

// Load from JSON
const jsonLoader = LoaderFactory.getLoader('json');
const jobConfig2 = await jsonLoader.load('./my-job.json');

// Auto-detection by file extension
const autoLoader = LoaderFactory.getLoaderForFile('./my-job.nomad');
const jobConfig3 = await autoLoader.load('./my-job.nomad');

// Parse and validate HCL content
const validatedJob = await nomad.jobs.parseAndValidateHCL(hclContent);

// Validate job specification
const validation = await nomad.jobs.validate({ Job: jobSpec });
if (validation.Valid) {
    console.log('Job specification is valid');
} else {
    console.error('Validation errors:', validation.ValidationErrors);
}

📚 API Reference

💼 Jobs Module (nomad.jobs)

Complete job lifecycle management with validation, parsing, and monitoring capabilities.

// CRUD Operations
await nomad.jobs.list();                    // List all jobs
await nomad.jobs.get('job-id');             // Get specific job
await nomad.jobs.create(jobSpec);           // Create new job
await nomad.jobs.update(jobSpec);           // Update existing job  
await nomad.jobs.delete('job-id');          // Delete job

// Validation & Parsing
await nomad.jobs.parse({ JobHCL: hclString }); // Parse HCL/JSON content
await nomad.jobs.validate({ Job: jobSpec }); // Validate job specification
await nomad.jobs.parseAndValidateHCL(hclContent); // Parse + validate HCL

// Job Operations
await nomad.jobs.plan(jobSpec);             // Plan job deployment
await nomad.jobs.stop('job-id');            // Stop running job
await nomad.jobs.restart('job-id');         // Restart job
await nomad.jobs.scale('job-id', 'web', 5); // Scale task group
await nomad.jobs.dispatch('job-id', payload); // Dispatch parameterized job

// Periodic Jobs
await nomad.jobs.periodicInfo('job-id');    // Get periodic job info
await nomad.jobs.periodicForce('job-id');   // Force periodic job run

// Monitoring & Health
await nomad.jobs.summary('job-id');         // Job summary
await nomad.jobs.versions('job-id');        // Job versions
await nomad.jobs.allocations('job-id');     // Job allocations
await nomad.jobs.evaluations('job-id');     // Job evaluations
await nomad.jobs.deployments('job-id');     // Job deployments
await nomad.jobs.isHealthy('job-id');       // Health status check
await nomad.jobs.getJobInfo('job-id');      // Complete job information

// Version Control
await nomad.jobs.revert('job-id', version); // Revert to specific version
await nomad.jobs.stable('job-id', version); // Mark version as stable

📦 Allocations Module (nomad.allocations)

Comprehensive allocation management with real-time monitoring and control capabilities.

// Management Operations
await nomad.allocations.list();                    // List all allocations
await nomad.allocations.list({ job: 'my-job' });   // Filter by job
await nomad.allocations.get('alloc-id');           // Get specific allocation
await nomad.allocations.stop('alloc-id');          // Stop allocation
await nomad.allocations.restart('alloc-id');       // Restart allocation

// Monitoring & Health
await nomad.allocations.stats('alloc-id');         // Raw resource statistics
await nomad.allocations.getFormattedStats('alloc-id'); // Formatted statistics
await nomad.allocations.isHealthy('alloc-id');     // Health check
await nomad.allocations.getSummary('alloc-id');    // Allocation summary

// Logs & Streaming
await nomad.allocations.logs('alloc-id', { task: 'web' }); // Raw log access
await nomad.allocations.getSimpleLogs('alloc-id', 'web', 'stdout'); // Simple log retrieval
const stopStream = await nomad.allocations.streamLogs(
    'alloc-id', 'web', 'stdout', 
    (data) => console.log(data)
); // Real-time log streaming

// Execution Operations
await nomad.allocations.exec('alloc-id', { Task: 'web', Command: ['ls'] }); // Execute command
await nomad.allocations.executeCommand('alloc-id', 'web', ['ls', '-la']); // Simple command execution
await nomad.allocations.signal('alloc-id', { Task: 'web', Signal: 'SIGUSR1' }); // Send signal

// Status & Synchronization
await nomad.allocations.waitForStatus('alloc-id', 'running', 30000); // Wait for status
await nomad.allocations.services('alloc-id');      // List allocation services

🔧 Tasks Module (nomad.tasks)

Task-level operations for fine-grained control and monitoring.

// Task Monitoring
await nomad.tasks.getLogs('alloc-id', 'task-name');    // Get task logs
await nomad.tasks.getStats('alloc-id', 'task-name');   // Resource statistics
await nomad.tasks.getStatus('alloc-id', 'task-name');  // Task status

// Task Operations
await nomad.tasks.restart('alloc-id', 'task-name');    // Restart specific task
await nomad.tasks.signal('alloc-id', 'task-name', 'SIGTERM'); // Send signal to task
await nomad.tasks.exec('alloc-id', 'task-name', ['ps', 'aux']); // Execute command in task

// Real-time Streaming
const stopStreaming = await nomad.tasks.streamLogs(
    'alloc-id', 
    'task-name', 
    'stdout',               // 'stdout' or 'stderr'
    (data) => console.log(data)
);

// Stop streaming when done
setTimeout(() => stopStreaming(), 60000);

📁 Namespaces Module (nomad.namespaces)

Multi-tenancy support with namespace management and resource isolation.

// CRUD Operations
await nomad.namespaces.list();              // List all namespaces
await nomad.namespaces.get('namespace');    // Get specific namespace
await nomad.namespaces.create({             // Create new namespace
    Name: 'production',
    Description: 'Production environment',
    Quota: 'prod-quota'                     // Optional resource quota
});
await nomad.namespaces.delete('namespace'); // Delete namespace

// Utility Operations
await nomad.namespaces.exists('namespace'); // Check if namespace exists
await nomad.namespaces.getJobs('namespace'); // Get jobs in namespace
await nomad.namespaces.getUsageStats('namespace'); // Resource usage statistics

// Example: Environment-based namespace setup
const environments = ['development', 'staging', 'production'];
for (const env of environments) {
    if (!await nomad.namespaces.exists(env)) {
        await nomad.namespaces.create({
            Name: env,
            Description: `${env.charAt(0).toUpperCase() + env.slice(1)} environment`
        });
    }
}

🖥️ Nodes Module (nomad.nodes)

Complete cluster infrastructure management with advanced drain operations and analytics.

// Node Management
await nomad.nodes.list();                   // List all cluster nodes
await nomad.nodes.list({ resources: true }); // Include resource information
await nomad.nodes.get('node-id');           // Get specific node details
await nomad.nodes.purge('node-id');         // Remove node (use with caution!)

// Monitoring & Statistics
await nomad.nodes.stats('node-id');         // Detailed node statistics
await nomad.nodes.allocations('node-id');   // Allocations running on node
await nomad.nodes.isNodeHealthy('node-id'); // Health status check
await nomad.nodes.getNodeSummary('node-id'); // Complete node summary

// Drain Management
await nomad.nodes.drain('node-id', {        // Configure node drain
    DrainSpec: {
        Deadline: 1800000000000,            // 30 minutes in nanoseconds
        IgnoreSystemJobs: false
    }
});
await nomad.nodes.drainSafely('node-id', 30); // Safe drain (30 min timeout)
await nomad.nodes.cancelDrain('node-id');    // Cancel active drain

// Node Eligibility
await nomad.nodes.markIneligible('node-id'); // Mark node as ineligible for scheduling
await nomad.nodes.markEligible('node-id');   // Mark node as eligible for scheduling
await nomad.nodes.toggleEligibility('node-id'); // Toggle eligibility status

// Cluster Analytics
await nomad.nodes.getClusterStats();        // Cluster-wide statistics
await nomad.nodes.getHealthyNodes();        // List of healthy nodes
await nomad.nodes.getNodesByClass('gpu');   // Filter nodes by node class
await nomad.nodes.getNodesByPool('prod');   // Filter nodes by node pool

// Example: Cluster maintenance workflow
async function performMaintenanceCheck() {
    const clusterStats = await nomad.nodes.getClusterStats();
    console.log(`Cluster: ${clusterStats.totalNodes} nodes, ${clusterStats.healthyNodes} healthy`);
    
    const nodes = await nomad.nodes.list({ resources: true });
    for (const node of nodes) {
        const isHealthy = await nomad.nodes.isNodeHealthy(node.ID);
        if (!isHealthy) {
            console.log(`⚠️ Node ${node.Name} requires attention`);
            await nomad.nodes.drainSafely(node.ID, 30);
        }
    }
}

🏊‍♂️ Node Pools Module (nomad.nodePools)

Advanced node pool management for multi-tenancy, resource allocation, and workload organization.

// CRUD Operations
await nomad.nodePools.list();               // List all node pools
await nomad.nodePools.get('pool-name');     // Get specific node pool
await nomad.nodePools.create({              // Create custom node pool
    Name: 'production',
    Description: 'Production workload pool',
    Meta: {
        'environment': 'prod',
        'tier': 'critical'
    }
});
await nomad.nodePools.update('pool-name', { // Update pool configuration
    Description: 'Updated pool description'
});
await nomad.nodePools.delete('pool-name');  // Delete node pool

// Specialized Pool Creation
await nomad.nodePools.createHAPool(         // High availability pool
    'ha-production',
    'HA pool for critical production workloads'
);
await nomad.nodePools.createDensePool(      // High density pool
    'development',
    'Development and testing pool',
    true                                    // Enable memory oversubscription
);
await nomad.nodePools.createWithScheduler(  // Custom scheduler configuration
    'gpu-workloads',
    'GPU-optimized workload pool',
    {
        SchedulerAlgorithm: 'spread',
        MemoryOversubscriptionEnabled: false
    }
);

// Analytics & Monitoring
await nomad.nodePools.getPoolUtilization('prod'); // Pool resource utilization
await nomad.nodePools.getPoolsOverview();   // Overview of all pools
await nomad.nodePools.getNodesInPool('prod'); // Nodes assigned to pool
await nomad.nodePools.getJobsInPool('prod'); // Jobs running in pool

// Pool Resource Management
await nomad.nodePools.listNodes('pool-name'); // List pool nodes
await nomad.nodePools.listJobs('pool-name');  // List pool jobs

// Example: Environment-based pool setup
async function setupEnvironmentPools() {
    const pools = [
        { name: 'production', description: 'Production workloads', ha: true },
        { name: 'staging', description: 'Staging environment', ha: false },
        { name: 'development', description: 'Development workloads', dense: true }
    ];
    
    for (const pool of pools) {
        const exists = await nomad.nodePools.get(pool.name).catch(() => null);
        if (!exists) {
            if (pool.ha) {
                await nomad.nodePools.createHAPool(pool.name, pool.description);
            } else if (pool.dense) {
                await nomad.nodePools.createDensePool(pool.name, pool.description, true);
            } else {
                await nomad.nodePools.create({
                    Name: pool.name,
                    Description: pool.description
                });
            }
            console.log(`✅ Created pool: ${pool.name}`);
        }
    }
}

🔧 Advanced Configuration

TLS/SSL Configuration

// Production TLS setup with certificates
const nomad = new NomadClient({
    baseUrl: 'https://nomad.example.com:4646',
    token: 'secure-token',
    tls: {
        caCertPath: '/etc/nomad/certs/ca.pem',
        clientCertPath: '/etc/nomad/certs/client.pem',
        clientKeyPath: '/etc/nomad/certs/client.key'
    }
});

// mTLS authentication (no token required)
const nomadMTLS = new NomadClient({
    baseUrl: 'https://nomad.example.com:4646',
    authMethod: 'mtls',
    tls: {
        caCertPath: '/etc/nomad/certs/ca.pem',
        clientCertPath: '/etc/nomad/certs/client.pem',
        clientKeyPath: '/etc/nomad/certs/client.key'
    }
});

Development Configuration

// Insecure configuration for development
const nomadDev = new NomadClient({
    baseUrl: 'https://nomad-dev:4646',
    token: 'dev-token',
    isTLSInsecure: true,  // Skip certificate validation
    namespace: 'development'
});

Timeouts and HTTP Configuration

const nomad = new NomadClient({
    baseUrl: 'http://nomad:4646',
    token: 'my-token',
    timeout: 60000,       // 60 seconds timeout
    namespace: 'production',
    region: 'us-west-1'
});

Factory Pattern Usage

import { NomadHttpClientFactory } from 'nomad-client';

// Create HTTP client with Bearer authentication
const httpClient = NomadHttpClientFactory.createWithBearer(
    'https://nomad.example.com:4646',
    'bearer-token-here'
);

// Create client with full authentication configuration
const httpClient2 = NomadHttpClientFactory.createWithAuth(
    'https://nomad.example.com:4646',
    'auth-token',
    'token',        // auth method: 'token' | 'bearer' | 'mtls'
    'production',   // namespace
    'us-west-1'     // region
);

📊 Monitoring and Observability

Health Checks and Status

// Cluster health verification
const health = await nomad.healthCheck();
console.log('Cluster healthy:', health.healthy);
console.log('Issues:', health.issues);

// Cluster information
const info = await nomad.getClusterInfo();
console.log('Nomad version:', info.version);
console.log('Region:', info.region);
console.log('Datacenter:', info.datacenter);

// Simple connectivity test
const isConnected = await nomad.ping();
console.log('Connection status:', isConnected);

Metrics and Statistics

// Cluster-wide metrics
const metrics = await nomad.getMetrics();
console.log('Resource utilization:', metrics);

// Detailed cluster status
const status = await nomad.getStatus();
console.log('Leader:', status.leader);
console.log('Servers:', status.servers.length);
console.log('Clients:', status.clients.length);

// Node pool statistics
const poolStats = await nomad.nodePools.getPoolsOverview();
console.log('Pools:', poolStats.pools.length);

// Job health monitoring
const jobHealth = await nomad.jobs.isHealthy('my-job');
console.log('Job is healthy:', jobHealth);

Real-time Monitoring Setup

async function setupMonitoring() {
    // Monitor job deployments
    const deployments = await nomad.jobs.deployments('web-app');
    for (const deployment of deployments) {
        if (deployment.Status === 'running') {
            console.log(`Deployment ${deployment.ID} is active`);
        }
    }
    
    // Monitor allocation health
    const allocations = await nomad.jobs.allocations('web-app');
    const healthyAllocs = [];
    
    for (const alloc of allocations) {
        const isHealthy = await nomad.allocations.isHealthy(alloc.ID);
        if (isHealthy) {
            healthyAllocs.push(alloc);
        } else {
            console.log(`⚠️ Allocation ${alloc.ID} is unhealthy`);
        }
    }
    
    console.log(`${healthyAllocs.length}/${allocations.length} allocations are healthy`);
}

🎯 Production Examples

Complete Deployment with Monitoring

async function deployWithMonitoring() {
    const nomad = new NomadClient({
        baseUrl: process.env.NOMAD_ADDR,
        token: process.env.NOMAD_TOKEN,
        namespace: process.env.NOMAD_NAMESPACE
    });
    
    // 1. Validate job specification
    const validation = await nomad.jobs.validate({ Job: jobSpec });
    if (!validation.Valid) {
        console.error('Job validation failed:', validation.ValidationErrors);
        return;
    }
    
    // 2. Plan deployment
    const plan = await nomad.jobs.plan(jobSpec);
    console.log(`Deployment will create ${plan.CreatedIndex} allocations`);
    
    // 3. Deploy and wait for completion
    const result = await nomad.deployAndWait(jobSpec, 300000);
    
    if (result.success) {
        console.log('✅ Deployment completed successfully!');
        
        // 4. Monitor allocation health
        for (const alloc of result.allocations || []) {
            const isHealthy = await nomad.allocations.isHealthy(alloc.ID);
            const stats = await nomad.allocations.getFormattedStats(alloc.ID);
            
            console.log(`Allocation ${alloc.ID}: ${isHealthy ? '✅' : '❌'}`);
            console.log(`  CPU: ${stats.cpu}%, Memory: ${stats.memory}%`);
        }
        
        // 5. Stream logs for monitoring
        const runningAlloc = result.allocations?.find(a => a.ClientStatus === 'running');
        if (runningAlloc) {
            const stopLogging = await nomad.tasks.streamLogs(
                runningAlloc.ID,
                'app',
                'stdout',
                (data) => console.log('APP LOG:', data)
            );
            
            // Stop logging after 5 minutes
            setTimeout(stopLogging, 300000);
        }
    } else {
        console.error('❌ Deployment failed:', result.error);
    }
}

Auto-scaling Implementation

interface ScalingConfig {
    minInstances: number;
    maxInstances: number;
    cpuThreshold: number;
    memoryThreshold: number;
    scaleUpBy: number;
    scaleDownBy: number;
}

async function autoScale(jobId: string, taskGroup: string, config: ScalingConfig) {
    const nomad = new NomadClient();
    
    // Get current allocations and metrics
    const allocations = await nomad.jobs.allocations(jobId);
    const runningAllocs = allocations.filter(a => a.ClientStatus === 'running');
    const currentCount = runningAllocs.length;
    
    console.log(`Current running instances: ${currentCount}`);
    
    // Calculate resource utilization
    let totalCPU = 0;
    let totalMemory = 0;
    let healthyInstances = 0;
    
    for (const alloc of runningAllocs) {
        const isHealthy = await nomad.allocations.isHealthy(alloc.ID);
        if (isHealthy) {
            const stats = await nomad.allocations.getFormattedStats(alloc.ID);
            totalCPU += stats.cpuPercent || 0;
            totalMemory += stats.memoryPercent || 0;
            healthyInstances++;
        }
    }
    
    if (healthyInstances === 0) {
        console.log('⚠️ No healthy instances found, skipping scaling');
        return;
    }
    
    const avgCPU = totalCPU / healthyInstances;
    const avgMemory = totalMemory / healthyInstances;
    
    console.log(`Average CPU: ${avgCPU.toFixed(1)}%, Memory: ${avgMemory.toFixed(1)}%`);
    
    // Determine scaling action
    let newCount = currentCount;
    
    if (avgCPU > config.cpuThreshold || avgMemory > config.memoryThreshold) {
        // Scale up
        newCount = Math.min(currentCount + config.scaleUpBy, config.maxInstances);
        console.log(`🔼 Scaling up due to high resource usage`);
    } else if (avgCPU < config.cpuThreshold * 0.5 && avgMemory < config.memoryThreshold * 0.5) {
        // Scale down (only when both CPU and memory are low)
        newCount = Math.max(currentCount - config.scaleDownBy, config.minInstances);
        console.log(`🔽 Scaling down due to low resource usage`);
    }
    
    if (newCount !== currentCount) {
        console.log(`Scaling from ${currentCount} to ${newCount} instances`);
        
        try {
            await nomad.jobs.scale(jobId, taskGroup, newCount);
            console.log(`✅ Successfully scaled ${jobId} to ${newCount} instances`);
        } catch (error) {
            console.error(`❌ Failed to scale ${jobId}:`, error);
        }
    } else {
        console.log('No scaling action needed');
    }
}

// Usage example
const scalingConfig: ScalingConfig = {
    minInstances: 2,
    maxInstances: 10,
    cpuThreshold: 75,
    memoryThreshold: 80,
    scaleUpBy: 2,
    scaleDownBy: 1
};

// Run auto-scaling every 5 minutes
setInterval(() => {
    autoScale('web-app', 'web', scalingConfig);
}, 300000);

Cluster Management and Health Monitoring

interface ClusterHealthReport {
    totalNodes: number;
    healthyNodes: number;
    drainingNodes: number;
    ineligibleNodes: number;
    issues: Array<{
        nodeId: string;
        nodeName: string;
        issue: string;
        severity: 'warning' | 'critical';
    }>;
}

async function comprehensiveClusterCheck(): Promise<ClusterHealthReport> {
    const nomad = new NomadClient();
    
    // 1. Get cluster overview
    const clusterStats = await nomad.nodes.getClusterStats();
    console.log(`\n📊 Cluster Overview:`);
    console.log(`  Total nodes: ${clusterStats.totalNodes}`);
    console.log(`  Healthy nodes: ${clusterStats.healthyNodes}`);
    
    const report: ClusterHealthReport = {
        totalNodes: clusterStats.totalNodes,
        healthyNodes: clusterStats.healthyNodes,
        drainingNodes: 0,
        ineligibleNodes: 0,
        issues: []
    };
    
    // 2. Detailed node analysis
    const nodes = await nomad.nodes.list({ resources: true });
    console.log(`\n🔍 Node Health Analysis:`);
    
    for (const node of nodes) {
        const isHealthy = await nomad.nodes.isNodeHealthy(node.ID);
        const nodeDetails = await nomad.nodes.get(node.ID);
        
        // Track node states
        if (nodeDetails.Drain) report.drainingNodes++;
        if (nodeDetails.SchedulingEligibility === 'ineligible') report.ineligibleNodes++;
        
        if (!isHealthy) {
            console.log(`  ⚠️ Node ${node.Name} (${node.ID.slice(0, 8)}) - UNHEALTHY`);
            
            report.issues.push({
                nodeId: node.ID,
                nodeName: node.Name,
                issue: 'Node health check failed',
                severity: 'critical'
            });
            
            // Check if we should drain the node
            const allocations = await nomad.nodes.allocations(node.ID);
            const runningAllocs = allocations.filter(a => a.ClientStatus === 'running');
            
            if (runningAllocs.length > 0 && !nodeDetails.Drain) {
                console.log(`    🚰 Initiating safe drain (${runningAllocs.length} allocations)`);
                await nomad.nodes.drainSafely(node.ID, 30); // 30 min deadline
            }
        } else {
            console.log(`  ✅ Node ${node.Name} (${node.ID.slice(0, 8)}) - HEALTHY`);
        }
    }
    
    // 3. Node pool balancing
    console.log(`\n🏊‍♂️ Node Pool Analysis:`);
    const poolsOverview = await nomad.nodePools.getPoolsOverview();
    
    for (const pool of poolsOverview.pools) {
        const utilization = await nomad.nodePools.getPoolUtilization(pool.name);
        console.log(`  Pool ${pool.name}:`);
        console.log(`    Nodes: ${pool.nodeCount}`);
        console.log(`    CPU utilization: ${utilization.capacityUtilization.cpu.toFixed(1)}%`);
        console.log(`    Memory utilization: ${utilization.capacityUtilization.memory.toFixed(1)}%`);
        
        // Alert on high utilization
        if (utilization.capacityUtilization.cpu > 85) {
            report.issues.push({
                nodeId: '',
                nodeName: pool.name,
                issue: `High CPU utilization in pool: ${utilization.capacityUtilization.cpu.toFixed(1)}%`,
                severity: 'warning'
            });
        }
    }
    
    // 4. Auto-create HA pool if production is under-provisioned
    const prodNodes = await nomad.nodes.getNodesByPool('production');
    if (prodNodes.length < 3) {
        console.log(`\n⚡ Creating HA pool for production (only ${prodNodes.length} nodes)`);
        
        try {
            await nomad.nodePools.createHAPool(
                'production-ha',
                'High availability pool for critical production workloads'
            );
            console.log('✅ HA production pool created successfully');
        } catch (error) {
            console.log('⚠️ HA pool already exists or creation failed');
        }
    }
    
    // 5. Generate summary
    console.log(`\n📋 Health Report Summary:`);
    console.log(`  Healthy: ${report.healthyNodes}/${report.totalNodes} nodes`);
    console.log(`  Draining: ${report.drainingNodes} nodes`);
    console.log(`  Ineligible: ${report.ineligibleNodes} nodes`);
    console.log(`  Issues found: ${report.issues.length}`);
    
    report.issues.forEach(issue => {
        const icon = issue.severity === 'critical' ? '🚨' : '⚠️';
        console.log(`    ${icon} ${issue.issue}`);
    });
    
    return report;
}

// Schedule regular cluster health checks
setInterval(async () => {
    console.log(`\n🔄 Starting scheduled cluster health check - ${new Date().toISOString()}`);
    await comprehensiveClusterCheck();
}, 600000); // Every 10 minutes

Advanced Pool Analytics and Capacity Management

interface PoolAnalytics {
    name: string;
    nodeCount: number;
    jobCount: number;
    utilization: {
        cpu: number;
        memory: number;
        disk: number;
    };
    recommendations: string[];
    status: 'healthy' | 'warning' | 'critical';
}

async function advancedPoolAnalytics(): Promise<PoolAnalytics[]> {
    const nomad = new NomadClient();
    const results: PoolAnalytics[] = [];
    
    // Get comprehensive pool overview
    const overview = await nomad.nodePools.getPoolsOverview();
    console.log('📊 Advanced Pool Analytics Report:');
    console.log('=' .repeat(50));
    
    for (const pool of overview.pools) {
        console.log(`\n🏊‍♂️ Analyzing Pool: ${pool.name}`);
        
        // Get detailed utilization metrics
        const utilization = await nomad.nodePools.getPoolUtilization(pool.name);
        const nodes = await nomad.nodePools.getNodesInPool(pool.name);
        const jobs = await nomad.nodePools.getJobsInPool(pool.name);
        
        const analytics: PoolAnalytics = {
            name: pool.name,
            nodeCount: nodes.length,
            jobCount: jobs.length,
            utilization: {
                cpu: utilization.capacityUtilization.cpu,
                memory: utilization.capacityUtilization.memory,
                disk: utilization.capacityUtilization.disk
            },
            recommendations: [],
            status: 'healthy'
        };
        
        // Resource utilization analysis
        console.log(`  📈 Resource Utilization:`);
        console.log(`    CPU: ${analytics.utilization.cpu.toFixed(1)}%`);
        console.log(`    Memory: ${analytics.utilization.memory.toFixed(1)}%`);
        console.log(`    Disk: ${analytics.utilization.disk.toFixed(1)}%`);
        console.log(`  📊 Workload Distribution:`);
        console.log(`    Active Nodes: ${analytics.nodeCount}`);
        console.log(`    Running Jobs: ${analytics.jobCount}`);
        
        // Generate recommendations and status
        if (analytics.utilization.cpu > 90 || analytics.utilization.memory > 90) {
            analytics.status = 'critical';
            analytics.recommendations.push('Immediate capacity expansion required');
            
            // Auto-create overflow pool for critical pools
            if (pool.name === 'production') {
                console.log(`  🚨 CRITICAL: Creating emergency overflow pool`);
                try {
                    await nomad.nodePools.createDensePool(
                        `${pool.name}-emergency`,
                        `Emergency overflow pool for ${pool.name}`,
                        true // Enable memory oversubscription for emergency capacity
                    );
                    console.log(`  ✅ Emergency pool created: ${pool.name}-emergency`);
                } catch (error) {
                    console.log(`  ⚠️ Emergency pool creation failed: ${error}`);
                }
            }
        } else if (analytics.utilization.cpu > 75 || analytics.utilization.memory > 80) {
            analytics.status = 'warning';
            analytics.recommendations.push('Consider adding more nodes to this pool');
            
            if (analytics.utilization.cpu > 85) {
                console.log(`  ⚠️ High CPU utilization - consider scaling`);
            }
            if (analytics.utilization.memory > 85) {
                console.log(`  ⚠️ High memory utilization - monitor closely`);
            }
        } else if (analytics.utilization.cpu < 25 && analytics.utilization.memory < 30) {
            analytics.recommendations.push('Pool may be over-provisioned');
            console.log(`  💡 Low utilization - consider consolidating workloads`);
        }
        
        // Node health analysis within pool
        let healthyNodes = 0;
        for (const node of nodes) {
            const isHealthy = await nomad.nodes.isNodeHealthy(node.ID);
            if (isHealthy) healthyNodes++;
        }
        
        const healthRatio = healthyNodes / nodes.length;
        console.log(`  🏥 Node Health: ${healthyNodes}/${nodes.length} (${(healthRatio * 100).toFixed(1)}%)`);
        
        if (healthRatio < 0.8) {
            analytics.status = 'critical';
            analytics.recommendations.push('Multiple unhealthy nodes detected');
        } else if (healthRatio < 0.9) {
            analytics.status = 'warning';
            analytics.recommendations.push('Some nodes require attention');
        }
        
        // Display recommendations
        if (analytics.recommendations.length > 0) {
            console.log(`  💡 Recommendations:`);
            analytics.recommendations.forEach(rec => 
                console.log(`    - ${rec}`)
            );
        }
        
        // Status indicator
        const statusIcon = {
            healthy: '✅',
            warning: '⚠️',
            critical: '🚨'
        }[analytics.status];
        
        console.log(`  ${statusIcon} Pool Status: ${analytics.status.toUpperCase()}`);
        
        results.push(analytics);
    }
    
    // Generate executive summary
    console.log(`\n📋 Executive Summary:`);
    console.log('=' .repeat(30));
    
    const healthyPools = results.filter(p => p.status === 'healthy').length;
    const warningPools = results.filter(p => p.status === 'warning').length;
    const criticalPools = results.filter(p => p.status === 'critical').length;
    
    console.log(`Total Pools: ${results.length}`);
    console.log(`✅ Healthy: ${healthyPools}`);
    console.log(`⚠️ Warning: ${warningPools}`);
    console.log(`🚨 Critical: ${criticalPools}`);
    
    const totalNodes = results.reduce((sum, p) => sum + p.nodeCount, 0);
    const totalJobs = results.reduce((sum, p) => sum + p.jobCount, 0);
    const avgCPU = results.reduce((sum, p) => sum + p.utilization.cpu, 0) / results.length;
    
    console.log(`\nCluster Totals:`);
    console.log(`  Nodes: ${totalNodes}`);
    console.log(`  Jobs: ${totalJobs}`);
    console.log(`  Average CPU: ${avgCPU.toFixed(1)}%`);
    
    return results;
}

// Schedule pool analytics every 15 minutes
setInterval(async () => {
    console.log(`\n🔍 Running scheduled pool analytics - ${new Date().toISOString()}`);
    await advancedPoolAnalytics();
}, 900000);

🏗️ Architecture

SOLID Principles Implementation

  • Single Responsibility: Each module has a specific, well-defined responsibility
  • Open/Closed: Extensible through interfaces and factory patterns
  • Liskov Substitution: All implementations are interchangeable via interfaces
  • Interface Segregation: Small, focused interfaces prevent unnecessary dependencies
  • Dependency Inversion: Dependencies are injected abstractions, not concrete implementations

Project Structure

src/
├── modules/                    # Core Nomad API modules
│   ├── job.ts                 # Job lifecycle management
│   ├── allocations.ts         # Allocation monitoring & control
│   ├── task.ts                # Task-level operations
│   ├── namespaces.ts          # Multi-tenancy support
│   ├── nodes.ts               # Cluster node management
│   └── provider.ts            # Main provider interface
├── core/                      # Core infrastructure
│   └── http/                  # HTTP client implementation
│       ├── HttpClient.ts      # Base HTTP client
│       ├── index.ts           # HTTP exports
│       └── factories/         # Client factory patterns
│           └── NomadHttpClientFactory.ts
├── config/                    # Configuration management
│   └── NomadConfig.ts         # Authentication & config
├── types/                     # TypeScript definitions
│   ├── common.ts              # Shared types
│   ├── job.ts                 # Job-specific types
│   └── index.ts               # Type exports
├── data-loaders/              # Configuration loaders
│   ├── hcl-loader.ts          # HCL file support
│   ├── json-loader.ts         # JSON configuration
│   └── yaml-loader.ts         # YAML configuration
└── examples/                  # Usage examples
    └── authentication-examples.ts

Design Patterns Used

  • Provider Pattern: Unified interface for all Nomad operations
  • Factory Pattern: HTTP client creation with different auth methods
  • Singleton Pattern: Configuration manager instance
  • Strategy Pattern: Multiple authentication strategies (Token, Bearer, mTLS)
  • Observer Pattern: Real-time log streaming with callbacks

🧪 Testing

Unit Testing Setup

import { NomadClient } from 'nomad-client';
import { jest } from '@jest/globals';

// Test configuration
const testConfig = {
    baseUrl: 'http://localhost:4646',
    token: 'test-token',
    namespace: 'test'
};

const testNomad = new NomadClient(testConfig);

// Mock HTTP client for unit tests
const mockHttpClient = {
    get: jest.fn(),
    post: jest.fn(),
    put: jest.fn(),
    delete: jest.fn(),
    stream: jest.fn()
};

// Example unit test
describe('NomadClient', () => {
    let nomad: NomadClient;
    
    beforeEach(() => {
        nomad = new NomadClient(testConfig);
        // Replace internal httpClient with mock
        (nomad as any).httpClient = mockHttpClient;
    });
    
    it('should create a job successfully', async () => {
        const mockResponse = { EvalID: 'eval-123' };
        mockHttpClient.post.mockResolvedValue({ data: mockResponse });
        
        const jobSpec = { ID: 'test-job', Type: 'service' };
        const result = await nomad.jobs.create(jobSpec);
        
        expect(mockHttpClient.post).toHaveBeenCalledWith('/v1/jobs', jobSpec);
        expect(result.EvalID).toBe('eval-123');
    });
});

Integration Testing

import { NomadClient } from 'nomad-client';

describe('Nomad Integration Tests', () => {
    let nomad: NomadClient;
    
    beforeAll(() => {
        // Use actual Nomad instance (requires running Nomad)
        nomad = new NomadClient({
            baseUrl: process.env.NOMAD_ADDR || 'http://localhost:4646',
            token: process.env.NOMAD_TOKEN
        });
    });
    
    it('should connect to Nomad cluster', async () => {
        const connected = await nomad.ping();
        expect(connected).toBe(true);
    });
    
    it('should list jobs', async () => {
        const jobs = await nomad.jobs.list();
        expect(Array.isArray(jobs)).toBe(true);
    });
});

Test Coverage

The library maintains high test coverage across all modules:

  • Jobs Module: 91.51% coverage
  • Allocations Module: 91.3% coverage
  • Tasks Module: 97.33% coverage
  • Client Module: 92.06% coverage
  • Nodes Module: 94.56% coverage
  • Authentication: 100% coverage

Run tests with:

npm test                    # Run all tests
npm run test:coverage      # Run with coverage report
npm run test:watch         # Watch mode for development

🤝 Contributing

We welcome contributions! Please follow these steps:

  1. Fork the Repository

    git fork https://github.com/your-org/nomad-client
  2. Create a Feature Branch

    git checkout -b feature/awesome-new-feature
  3. Make Your Changes

    • Follow TypeScript and ESLint conventions
    • Add tests for new functionality
    • Update documentation as needed
  4. Test Your Changes

    npm test
    npm run lint
    npm run build
  5. Commit and Push

    git commit -am 'feat: add awesome new feature'
    git push origin feature/awesome-new-feature
  6. Open a Pull Request

    • Provide clear description of changes
    • Include test coverage information
    • Reference any related issues

Development Setup

# Clone and setup
git clone https://github.com/your-org/nomad-client
cd nomad-client
npm install

# Run tests
npm test

# Build library
npm run build

# Start development mode
npm run dev

📄 License

MIT License - see LICENSE file for details.

This project is licensed under the MIT License, which means you can use, modify, and distribute it freely for both commercial and non-commercial purposes.


🔗 Documentation Links

Official HashiCorp Resources

Library Resources


💡 Support & Community

Getting Help

Community Guidelines

  • Be respectful and inclusive
  • Search existing issues before creating new ones
  • Provide minimal reproducible examples for bugs
  • Follow our Code of Conduct

🏆 Acknowledgments

  • HashiCorp Team - For creating and maintaining Nomad
  • TypeScript Community - For excellent tooling and ecosystem
  • Contributors - Everyone who has contributed to this project

📊 Project Status

npm version Build Status Test Coverage TypeScript License

Built with ❤️ for the HashiCorp Nomad community