@collecteverything/service-registry-client
v1.1.0
Published
Client TypeScript pour Service Registry multi-tenant
Maintainers
Readme
Service Registry Client
Modern TypeScript client for service discovery with automatic registration. Perfect for microservice architectures on Kubernetes with multi-tenancy, intelligent load balancing and automated lifecycle management.
Installation
npm install @collecteverything/service-registry-clientMain Features
- Auto-registration via environment variables (zero config)
- Service discovery with intelligent load balancing
- Database discovery multi-tenant (MongoDB, Redis)
- Automatic JWT authentication
- Automatic heartbeat to keep services alive
- Robust error handling with automatic retry
- Intelligent caching for performance optimization
- Real-time events for monitoring
- Kubernetes compatible out-of-the-box
- TypeScript with complete types
Quick Start
Auto-register in 2 lines of code
// In your Express/Fastify/NestJS service
import { createAutoRegisterClient, createK8sConfig } from '@collecteverything/service-registry-client';
app.listen(3000, async () => {
console.log('Server started on port 3000');
// Automatic auto-register!
await createAutoRegisterClient(createK8sConfig());
console.log('Service registered automatically');
});That's it! Your service is now visible to all other services in the cluster.
Required environment variables
# Required variables
AUTO_REGISTER=true
SERVICE_NAME=user-api
# Variables with intelligent defaults
SERVICE_PORT=3000 # Your service port
SERVICE_VERSION=1.0.0 # Semver version
SERVICE_TYPE=service # "service" or "database"
# Optional variables for enrichment
SERVICE_TAGS=api,users,v1 # Tags for discovery
TENANT_ID=production # Multi-tenant isolation
REGISTRY_URL=http://service-registry.kube-system:8084 # Registry URLKubernetes Deployment
Service Registry (deploy once)
# registry-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-registry
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: service-registry
template:
spec:
containers:
- name: service-registry
image: votre-registry:latest
ports:
- containerPort: 8084
---
apiVersion: v1
kind: Service
metadata:
name: service-registry
namespace: kube-system
spec:
selector:
app: service-registry
ports:
- port: 8084
targetPort: 8084
type: ClusterIPYour services (with auto-register enabled)
# user-api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-api
spec:
replicas: 3
template:
spec:
containers:
- name: user-api
image: votre-user-api:latest
env:
# Auto-register configuration
- name: AUTO_REGISTER
value: "true"
- name: SERVICE_NAME
value: "user-api"
- name: SERVICE_PORT
value: "3000"
- name: SERVICE_VERSION
value: "1.2.0"
- name: SERVICE_TAGS
value: "api,users,authentication"
- name: TENANT_ID
value: "production"
# Automatic Kubernetes variables
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespaceAdvanced Usage Guide
Manual client configuration
import { ServiceRegistryClient } from '@collecteverything/service-registry-client';
const client = new ServiceRegistryClient({
baseUrl: 'http://service-registry.kube-system:8084',
authToken: process.env.JWT_TOKEN,
timeout: 5000,
heartbeatInterval: 30000, // 30 secondes
retries: 3
});Manual service registration
import { generateInstanceId } from '@collecteverything/service-registry-client';
const serviceInfo = await client.registerService({
instanceId: generateInstanceId(),
name: 'payment-api',
version: '2.1.0',
host: process.env.POD_IP || 'localhost',
port: 3001,
protocol: 'http',
path: '/api/v2',
tenantId: 'production',
tags: ['api', 'payments', 'critical'],
metadata: {
environment: 'production',
region: 'eu-west-1'
}
}, {
autoStart: true // Start heartbeat automatically
});
console.log('Service registered:', serviceInfo.url);Discover and use other services
// Find all email services
const emailServices = await client.discoverServices({
name: 'email-service',
tags: ['notifications']
});
// Select a service with load balancing
const selectedService = await client.selectService({
name: 'email-service',
strategy: 'health-based' // 'round-robin', 'random', 'health-based'
});
if (selectedService) {
console.log('Selected service:', selectedService.url);
// Use the discovered service
const response = await fetch(`${selectedService.url}/send-email`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ to: '[email protected]', subject: 'Hello' })
});
}Discover databases
// Register a MongoDB database (in a DB pod)
await client.registerDatabase({
instanceId: process.env.HOSTNAME,
name: 'user-database',
databaseName: 'users_prod',
version: '1.0.0',
host: process.env.POD_IP,
port: 27017,
protocol: 'mongodb',
databaseType: 'mongodb',
tenantId: 'production',
tags: ['primary', 'users'],
maxConnections: 100
});
// Dans un autre service, récupérer la connection string
const connection = await client.getDatabaseConnection({
databaseName: 'user-database',
databaseType: 'mongodb'
});
if (connection) {
console.log('MongoDB connection:', connection.connectionString);
// mongodb://10.244.1.15:27017/users_prod
// Use with mongoose
await mongoose.connect(connection.connectionString);
}Complete Architecture
import express from 'express';
import { createAutoRegisterClient, createK8sConfig } from '@collecteverything/service-registry-client';
class OrderService {
private app = express();
private registryClient?: ServiceRegistryClient;
async initialize() {
// Setup routes
this.setupRoutes();
// Start server
this.app.listen(3000, async () => {
console.log('Order Service started');
// Auto-register
this.registryClient = await createAutoRegisterClient(createK8sConfig());
console.log('Service registered in registry');
// Discover infrastructure
await this.discoverDependencies();
});
}
private setupRoutes() {
this.app.get('/health', (req, res) => {
res.json({ status: 'healthy', timestamp: new Date().toISOString() });
});
this.app.post('/orders', async (req, res) => {
try {
// Discover payment service
const paymentService = await this.registryClient!.selectService({
name: 'payment-api',
strategy: 'health-based'
});
if (!paymentService) {
return res.status(503).json({ error: 'Payment service unavailable' });
}
// Call payment service
const paymentResponse = await fetch(`${paymentService.url}/process-payment`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(req.body)
});
const result = await paymentResponse.json();
res.json(result);
} catch (error) {
res.status(500).json({ error: 'Order processing failed' });
}
});
}
private async discoverDependencies() {
// Discover all infrastructure
const infrastructure = await this.registryClient!.discoverAll();
console.log(`Infrastructure discovered:`);
console.log(`- ${infrastructure.summary.totalServices} services`);
console.log(`- ${infrastructure.summary.totalDatabases} databases`);
// Configure database connections
for (const db of infrastructure.databases) {
if (db.databaseType === 'mongodb' && db.name === 'orders-db') {
await mongoose.connect(db.connectionString);
console.log('Connected to orders database');
}
}
}
async shutdown() {
if (this.registryClient) {
await this.registryClient.close();
}
}
}
// Startup
const orderService = new OrderService();
orderService.initialize();
// Graceful shutdown
process.on('SIGTERM', () => orderService.shutdown());API Reference
Main methods
createAutoRegisterClient(config)
Creates a client and performs auto-register in one step.
client.autoRegisterFromEnv()
Registers the service based on environment variables.
client.discoverServices(request)
Discover services based on criteria.
client.selectService(request)
Select a service with load balancing.
client.discoverDatabases(request)
Discover databases by tenant.
client.getDatabaseConnection(request)
Get a connection string.
Configuration helpers
// For local development
import { createDevConfig } from '@collecteverything/service-registry-client';
const config = createDevConfig('http://localhost:8084');
// For Kubernetes
import { createK8sConfig } from '@collecteverything/service-registry-client';
const config = createK8sConfig(jwtToken);Error Handling
import {
isConnectionError,
isAuthenticationError,
isValidationError
} from '@collecteverything/service-registry-client';
try {
const client = await createAutoRegisterClient(config);
} catch (error) {
if (isConnectionError(error)) {
console.error('Registry unreachable:', error.message);
// Service can continue to function without registry
} else if (isAuthenticationError(error)) {
console.error('Invalid JWT token:', error.message);
} else if (isValidationError(error)) {
console.error('Invalid service configuration:', error.response);
}
}Monitoring and Events
client.on('service:registered', ({ service }) => {
console.log(`Service registered: ${service.name}`);
});
client.on('service:discovered', ({ services }) => {
console.log(`Discovered ${services.length} services`);
});
client.on('heartbeat:failed', ({ instanceId, error }) => {
console.error(`Heartbeat failed for ${instanceId}`);
});
// Registry statistics
const stats = await client.getStats();
console.log(`Registry stats: ${stats.healthy.services} healthy services`);Multi-tenant
The client automatically handles tenant isolation:
// JWT must contain tenantId in 'websiteId' or 'tenantId' claim
const client = new ServiceRegistryClient({
baseUrl: 'http://registry:8084',
authToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...' // { tenantId: "company-abc" }
});
// All discoveries are automatically filtered by tenant
const services = await client.discoverServices({ name: 'user-api' });
// Only returns services from tenant "company-abc"Complete examples
Simple Express service
import express from 'express';
import { createAutoRegisterClient } from '@collecteverything/service-registry-client';
const app = express();
app.get('/health', (req, res) => res.json({ status: 'ok' }));
app.get('/api/users', (req, res) => res.json({ users: [] }));
app.listen(3000, async () => {
// Required environment variables:
// AUTO_REGISTER=true
// SERVICE_NAME=user-api
// SERVICE_PORT=3000
await createAutoRegisterClient({
baseUrl: process.env.REGISTRY_URL || 'http://localhost:8084'
});
console.log('User API ready and registered');
});MongoDB Database
// In a MongoDB pod with your client
import { createAutoRegisterClient } from '@collecteverything/service-registry-client';
// Environment variables:
// AUTO_REGISTER=true
// SERVICE_TYPE=database
// SERVICE_NAME=user-db
// DATABASE_TYPE=mongodb
// DATABASE_NAME=users_production
// SERVICE_PORT=27017
await createAutoRegisterClient({
baseUrl: 'http://service-registry.kube-system:8084'
});
console.log('MongoDB registered in service registry');Changelog
v1.0.0
- Automatic auto-registration via environment variables
- Complete Kubernetes support with POD_IP, HOSTNAME
- Service and database discovery
- Smart load balancing (round-robin, random, health-based)
- Automatic heartbeat with MongoDB TTL
- Multi-tenant JWT authentication
- Robust error handling with retry
- Smart cache with TTL
- Complete TypeScript types
Usage in your projects
- Install the library :
npm install @collecteverything/service-registry-client - Add 2 lines in your service startup
- Configure environment variables in your K8s deployments
- Deploy: auto-register works automatically
That's it! Services automatically discover each other.
