@ticatec/consul-client
v1.0.0
Published
A lightweight, robust TypeScript client for HashiCorp Consul HTTP API, featuring comprehensive KV store management, connection pooling, and dual ESM/CommonJS builds.
Downloads
181
Readme
Ticatec Consul Client
中文 | English
A lightweight, high-performance TypeScript client for the HashiCorp Consul HTTP API, featuring comprehensive Key-Value (KV) management, native HTTP Blocking Query change monitoring (watch), automatic Base64 decoding, HTTP/HTTPS connection pooling, and built-in safe console logging.
Features
- ✅ Dual Module Support: Full ES Module (ESM) and CommonJS (CJS) dual build with cross-module singleton state.
- ✅ Zero Runtime Dependencies: Powered entirely by Node.js built-in networking (
node:http,node:https), no external dependencies. - ✅ KV Store Management: Intuitive API for
get,getValue,getWithMeta,set,delete/del, andkeys. - ✅ Automatic Base64 Decoding: Decodes Consul's Base64
Valueinto UTF-8 strings orBufferobjects automatically. - ✅ Real-Time Watch: Native long-polling (Blocking Query) change watcher with exponential backoff retry and rate limiting protection against caching proxies.
- ✅ Connection Pooling: Built-in HTTP and HTTPS agents with keep-alive enabled by default for high-throughput internal network access.
- ✅ Singleton & Multi-Instance Registry: Named singletons (
ConsulClient.getInstance('app')) or independent instances (new ConsulClient()). - ✅ Built-in Safe Console Logging: Standard zero-dependency console output filtered by
LOG_LEVEL. ACL tokens are strictly kept out of logs. - ✅ Environment Variables Ready: Automatically resolves
CONSUL_HTTP_ADDR,CONSUL_HOST,CONSUL_PORT,CONSUL_HTTP_SSL, andCONSUL_HTTP_TOKEN.
Installation
pnpm add @ticatec/consul-client
# or npm
npm install @ticatec/consul-clientThis package is 100% self-contained with zero runtime external dependencies. Because application logging configurations (e.g. Pino) are often loaded from Consul at bootstrap, consul-client only relies on Node.js built-in console output (filtered by process.env.LOG_LEVEL), avoiding chicken-and-egg startup dependencies. You may also supply an optional custom logger via options.logger.
Quick Start
Basic Usage
Named import (recommended):
import { ConsulClient } from '@ticatec/consul-client';CommonJS (CJS):
const { ConsulClient } = require('@ticatec/consul-client');Initialize Client
// Uses default host (127.0.0.1:8500) or env variables (CONSUL_HTTP_ADDR, etc.)
const consul = new ConsulClient();
// Or explicit configuration:
const consul = new ConsulClient({
host: 'consul.service.internal',
port: 8500,
token: 'my-acl-token',
timeout: 10000
});Key-Value (KV) Operations
Read Values (get & getValue)
// 1. Get KV item with metadata (automatically Base64 decoded)
const item = await consul.kv.get('config/app.yaml');
if (item) {
console.log(item.Key); // 'config/app.yaml'
console.log(item.Value); // 'port: 8080\nname: demo'
console.log(item.ModifyIndex); // 1024
}
// 2. Convenience helper: retrieve only the value
const yamlStr = await consul.kv.getValue('config/app.yaml');
// 3. Retrieve and parse JSON directly
const config = await consul.kv.getValue<{ port: number }>('config/app.json', {
json: true,
defaultValue: { port: 3000 }
});
// 4. Retrieve binary data as Buffer
const bin = await consul.kv.get('certs/server.crt', { buffer: true });
// 5. Retrieve all keys under prefix
const items = await consul.kv.get('config/', { recurse: true });
for (const entry of items) {
console.log(entry.Key, entry.Value);
}
// 6. List key names only
const keys = await consul.kv.keys('config/');
console.log(keys); // ['config/app.yaml', 'config/db.yaml']Write and Delete Values
// Save string value
await consul.kv.set('config/app.yaml', 'port: 8080');
// Save JSON object (automatically serialized)
await consul.kv.set('config/app.json', { port: 8080, env: 'production' });
// Atomic update using CAS (Compare-And-Swap)
const updated = await consul.kv.set('config/lock', 'instance-1', { cas: item.ModifyIndex });
if (!updated) {
console.warn('CAS mismatch! Key was modified by another process.');
}
// Delete key
await consul.kv.delete('config/old-key'); // or alias consul.kv.del()
// Delete prefix recursively
await consul.kv.delete('config/staging/', { recurse: true });Real-Time Watch (consul.watch)
Consul uses Blocking Queries to monitor changes. The connection is held open on the server until a change occurs or the wait timeout is reached. It consumes virtually zero CPU on both client and server.
// 1. Watch a key for changes
const watcher = consul.watch({
key: 'config/app.yaml',
wait: '5m', // Blocking query duration (default '5m', max '10m')
minIntervalMs: 1000 // Rate-limiting guard against caching proxies (default 1000ms)
});
watcher.on('change', (item, meta) => {
console.log(`Config updated (Index: ${meta.index}):`, item?.Value);
});
watcher.on('error', (err) => {
console.error('Watch error (will auto-retry with exponential backoff):', err);
});
// 2. Shortcut helper: watch value directly
const valWatcher = consul.watchValue('config/feature-flags.json', (newVal) => {
console.log('Feature flags reloaded:', newVal);
});
// Stop watching when shutting down
watcher.end();
// Or close the client, which automatically stops all active watchers
consul.close();Singleton & Multi-Instance Registry
The client registry is preserved on globalThis under Symbol.for('@ticatec/consul-client.instances'), so ESM and CJS modules share the same instances:
// Initialize singleton at application bootstrap
ConsulClient.init({
host: process.env.CONSUL_HOST,
token: process.env.CONSUL_TOKEN
});
// Retrieve singleton in any controller or service
const consul = ConsulClient.getInstance();
// Or multi-instance named registry
ConsulClient.init('east', { host: 'consul-east.internal' });
ConsulClient.init('west', { host: 'consul-west.internal' });
const eastConsul = ConsulClient.getInstance('east');License
MIT © Henry Feng / Ticatec
