navis.js 1.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -20
- package/bin/generators/service.js +167 -0
- package/bin/navis.js +56 -2
- package/examples/v2-features-demo.js +190 -0
- package/examples/v3-features-demo.js +226 -0
- package/package.json +1 -1
- package/src/index.js +50 -12
- package/src/messaging/base-messaging.js +82 -0
- package/src/messaging/kafka-adapter.js +152 -0
- package/src/messaging/nats-adapter.js +141 -0
- package/src/messaging/sqs-adapter.js +175 -0
- package/src/observability/logger.js +141 -0
- package/src/observability/metrics.js +193 -0
- package/src/observability/tracer.js +205 -0
- package/src/utils/circuit-breaker.js +107 -0
- package/src/utils/retry.js +88 -0
- package/src/utils/service-client.js +234 -104
- package/src/utils/service-config.js +88 -0
- package/src/utils/service-discovery.js +184 -0
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A lightweight, serverless-first, microservice API framework designed for AWS Lambda and Node.js.
|
|
4
4
|
|
|
5
|
+
**Author:** Syed Iman Ali
|
|
6
|
+
|
|
5
7
|
## Philosophy
|
|
6
8
|
|
|
7
9
|
Navis.js is "Express for serverless microservices — but simpler."
|
|
@@ -71,11 +73,20 @@ exports.handler = async (event) => {
|
|
|
71
73
|
```bash
|
|
72
74
|
# Start example server
|
|
73
75
|
navis start
|
|
76
|
+
|
|
77
|
+
# Generate a new microservice (v2)
|
|
78
|
+
navis generate service my-service
|
|
79
|
+
|
|
80
|
+
# Run verification tests (v3)
|
|
81
|
+
navis test
|
|
82
|
+
|
|
83
|
+
# Show metrics endpoint info (v3)
|
|
84
|
+
navis metrics
|
|
74
85
|
```
|
|
75
86
|
|
|
76
87
|
## Features
|
|
77
88
|
|
|
78
|
-
### v1
|
|
89
|
+
### v1
|
|
79
90
|
|
|
80
91
|
- ✅ HTTP routing (GET, POST, PUT, DELETE)
|
|
81
92
|
- ✅ Middleware support (`app.use()`)
|
|
@@ -84,14 +95,22 @@ navis start
|
|
|
84
95
|
- ✅ ServiceClient for service-to-service calls
|
|
85
96
|
- ✅ Timeout support
|
|
86
97
|
|
|
87
|
-
### v2
|
|
98
|
+
### v2
|
|
99
|
+
|
|
100
|
+
- ✅ **Retry logic** - Automatic retry with exponential backoff
|
|
101
|
+
- ✅ **Circuit breaker** - Prevents cascading failures
|
|
102
|
+
- ✅ **Config-based services** - Centralized service configuration
|
|
103
|
+
- ✅ **Service discovery** - Health checks and load balancing
|
|
104
|
+
- ✅ **Additional HTTP methods** - PUT, DELETE, PATCH support
|
|
105
|
+
- ✅ **CLI generators** - `navis generate service` command
|
|
88
106
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
107
|
+
### v3 (Current)
|
|
108
|
+
|
|
109
|
+
- ✅ **Async messaging** - SQS, Kafka, and NATS adapters
|
|
110
|
+
- ✅ **Structured logging** - Multi-level logging with context
|
|
111
|
+
- ✅ **Metrics collection** - Counters, gauges, histograms with Prometheus export
|
|
112
|
+
- ✅ **Distributed tracing** - Trace and span management
|
|
113
|
+
- ✅ **Enhanced CLI** - Test and metrics commands
|
|
95
114
|
|
|
96
115
|
## API Reference
|
|
97
116
|
|
|
@@ -107,19 +126,66 @@ navis start
|
|
|
107
126
|
- `app.listen(port, callback)` - Start HTTP server (Node.js)
|
|
108
127
|
- `app.handleLambda(event)` - Handle AWS Lambda event
|
|
109
128
|
|
|
110
|
-
### ServiceClient
|
|
129
|
+
### ServiceClient (v2 Enhanced)
|
|
111
130
|
|
|
131
|
+
```javascript
|
|
112
132
|
const { ServiceClient } = require('navis.js');
|
|
113
133
|
|
|
134
|
+
// Basic usage
|
|
114
135
|
const client = new ServiceClient('http://api.example.com', {
|
|
115
|
-
timeout: 5000,
|
|
136
|
+
timeout: 5000,
|
|
116
137
|
});
|
|
117
138
|
|
|
118
|
-
//
|
|
119
|
-
const
|
|
139
|
+
// With retry and circuit breaker (v2)
|
|
140
|
+
const resilientClient = new ServiceClient('http://api.example.com', {
|
|
141
|
+
timeout: 5000,
|
|
142
|
+
maxRetries: 3,
|
|
143
|
+
retryBaseDelay: 1000,
|
|
144
|
+
circuitBreaker: {
|
|
145
|
+
failureThreshold: 5,
|
|
146
|
+
resetTimeout: 60000,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// All HTTP methods (v2)
|
|
151
|
+
await client.get('/users');
|
|
152
|
+
await client.post('/users', { name: 'John' });
|
|
153
|
+
await client.put('/users/1', { name: 'Jane' }); // v2
|
|
154
|
+
await client.patch('/users/1', { name: 'Bob' }); // v2
|
|
155
|
+
await client.delete('/users/1'); // v2
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Service Configuration (v2)
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
const { ServiceConfig, ServiceClient } = require('navis.js');
|
|
162
|
+
|
|
163
|
+
const config = new ServiceConfig({
|
|
164
|
+
defaultOptions: {
|
|
165
|
+
timeout: 5000,
|
|
166
|
+
retry: { maxRetries: 3 },
|
|
167
|
+
circuitBreaker: { failureThreshold: 5 },
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
config.register('userService', 'http://localhost:3001');
|
|
172
|
+
const userConfig = config.get('userService');
|
|
173
|
+
const client = new ServiceClient(userConfig.baseUrl, userConfig);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Service Discovery (v2)
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
const { ServiceDiscovery, ServiceClient } = require('navis.js');
|
|
180
|
+
|
|
181
|
+
const discovery = new ServiceDiscovery();
|
|
182
|
+
discovery.register('api', [
|
|
183
|
+
'http://api1.example.com',
|
|
184
|
+
'http://api2.example.com',
|
|
185
|
+
]);
|
|
120
186
|
|
|
121
|
-
//
|
|
122
|
-
const
|
|
187
|
+
const url = discovery.getNext('api'); // Round-robin
|
|
188
|
+
const client = new ServiceClient(url);
|
|
123
189
|
```
|
|
124
190
|
|
|
125
191
|
### Response Helpers
|
|
@@ -134,6 +200,54 @@ response.success(res, { data: 'value' }, 200);
|
|
|
134
200
|
response.error(res, 'Error message', 500);
|
|
135
201
|
```
|
|
136
202
|
|
|
203
|
+
### Observability (v3)
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
const { Logger, Metrics, Tracer } = require('navis.js');
|
|
207
|
+
|
|
208
|
+
// Structured logging
|
|
209
|
+
const logger = new Logger({ level: 'INFO', context: { service: 'api' } });
|
|
210
|
+
logger.info('User logged in', { userId: 123 });
|
|
211
|
+
|
|
212
|
+
// Metrics collection
|
|
213
|
+
const metrics = new Metrics();
|
|
214
|
+
metrics.increment('api_calls', 1, { endpoint: '/users' });
|
|
215
|
+
metrics.recordRequest('GET', '/users', 150, 200);
|
|
216
|
+
|
|
217
|
+
// Expose Prometheus metrics
|
|
218
|
+
app.get('/metrics', (req, res) => {
|
|
219
|
+
res.setHeader('Content-Type', 'text/plain');
|
|
220
|
+
res.end(metrics.toPrometheus());
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Distributed tracing
|
|
224
|
+
const tracer = new Tracer({ serviceName: 'api' });
|
|
225
|
+
const traceId = tracer.startTrace('user-operation');
|
|
226
|
+
const spanId = tracer.startSpan('db-query', { traceId });
|
|
227
|
+
tracer.finishSpan(spanId, { status: 'ok' });
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Async Messaging (v3)
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
const { SQSMessaging, KafkaMessaging, NATSMessaging } = require('navis.js');
|
|
234
|
+
|
|
235
|
+
// AWS SQS (requires @aws-sdk/client-sqs)
|
|
236
|
+
const sqs = new SQSMessaging({ region: 'us-east-1' });
|
|
237
|
+
await sqs.connect();
|
|
238
|
+
await sqs.publish(queueUrl, { userId: 123, action: 'user.created' });
|
|
239
|
+
|
|
240
|
+
// Kafka (requires kafkajs)
|
|
241
|
+
const kafka = new KafkaMessaging({ brokers: ['localhost:9092'] });
|
|
242
|
+
await kafka.connect();
|
|
243
|
+
await kafka.publish('user-events', { userId: 123, event: 'created' });
|
|
244
|
+
|
|
245
|
+
// NATS (requires nats)
|
|
246
|
+
const nats = new NATSMessaging({ servers: ['nats://localhost:4222'] });
|
|
247
|
+
await nats.connect();
|
|
248
|
+
await nats.publish('user.created', { userId: 123 });
|
|
249
|
+
```
|
|
250
|
+
|
|
137
251
|
## Examples
|
|
138
252
|
|
|
139
253
|
See the `examples/` directory:
|
|
@@ -141,18 +255,33 @@ See the `examples/` directory:
|
|
|
141
255
|
- `server.js` - Node.js HTTP server example
|
|
142
256
|
- `lambda.js` - AWS Lambda handler example
|
|
143
257
|
- `service-client-demo.js` - ServiceClient usage example
|
|
258
|
+
- `v2-features-demo.js` - v2 features demonstration (retry, circuit breaker, etc.)
|
|
259
|
+
- `v3-features-demo.js` - v3 features demonstration (messaging, observability, etc.)
|
|
144
260
|
|
|
145
261
|
## Roadmap
|
|
146
262
|
|
|
147
|
-
### v1
|
|
263
|
+
### v1 ✅
|
|
148
264
|
Core functionality: routing, middleware, Lambda support, ServiceClient
|
|
149
265
|
|
|
150
|
-
### v2
|
|
151
|
-
Resilience patterns: retry, circuit breaker, service discovery
|
|
266
|
+
### v2 ✅
|
|
267
|
+
Resilience patterns: retry, circuit breaker, service discovery, CLI generators
|
|
268
|
+
|
|
269
|
+
### v3 ✅ (Current)
|
|
270
|
+
Advanced features: async messaging (SQS/Kafka/NATS), observability, enhanced CLI
|
|
152
271
|
|
|
153
|
-
|
|
154
|
-
|
|
272
|
+
## Documentation
|
|
273
|
+
|
|
274
|
+
- [V2 Features Guide](./V2_FEATURES.md) - Complete v2 features documentation
|
|
275
|
+
- [V3 Features Guide](./V3_FEATURES.md) - Complete v3 features documentation
|
|
276
|
+
- [Verification Guide v2](./VERIFY_V2.md) - How to verify v2 features
|
|
277
|
+
- [Verification Guide v3](./VERIFY_V3.md) - How to verify v3 features
|
|
155
278
|
|
|
156
279
|
## License
|
|
157
280
|
|
|
158
|
-
MIT
|
|
281
|
+
MIT
|
|
282
|
+
|
|
283
|
+
## Author
|
|
284
|
+
|
|
285
|
+
**Syed Iman Ali**
|
|
286
|
+
|
|
287
|
+
Created with ❤️ for the serverless microservices community.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Generator
|
|
3
|
+
* v2: Generate service boilerplate
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generate service boilerplate
|
|
11
|
+
* @param {string} serviceName - Name of the service
|
|
12
|
+
* @param {string} targetDir - Target directory (default: current directory)
|
|
13
|
+
*/
|
|
14
|
+
function generateService(serviceName, targetDir = process.cwd()) {
|
|
15
|
+
const serviceDir = path.join(targetDir, serviceName);
|
|
16
|
+
|
|
17
|
+
// Create service directory
|
|
18
|
+
if (!fs.existsSync(serviceDir)) {
|
|
19
|
+
fs.mkdirSync(serviceDir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Generate service.js
|
|
23
|
+
const serviceTemplate = `const { NavisApp, response } = require('navis.js');
|
|
24
|
+
|
|
25
|
+
const app = new NavisApp();
|
|
26
|
+
|
|
27
|
+
// Middleware
|
|
28
|
+
app.use((req, res, next) => {
|
|
29
|
+
console.log(\`\${req.method} \${req.url}\`);
|
|
30
|
+
next();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Routes
|
|
34
|
+
app.get('/', (req, res) => {
|
|
35
|
+
response.success(res, {
|
|
36
|
+
service: '${serviceName}',
|
|
37
|
+
message: 'Welcome to ${serviceName} service',
|
|
38
|
+
version: '1.0.0'
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
app.get('/health', (req, res) => {
|
|
43
|
+
response.success(res, { status: 'ok', service: '${serviceName}' });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Add your routes here
|
|
47
|
+
// app.get('/api/users', (req, res) => { ... });
|
|
48
|
+
// app.post('/api/users', (req, res) => { ... });
|
|
49
|
+
|
|
50
|
+
// Start server
|
|
51
|
+
const PORT = process.env.PORT || 3000;
|
|
52
|
+
app.listen(PORT, () => {
|
|
53
|
+
console.log(\`${serviceName} service running on http://localhost:\${PORT}\`);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
module.exports = app;
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
fs.writeFileSync(path.join(serviceDir, 'service.js'), serviceTemplate);
|
|
60
|
+
|
|
61
|
+
// Generate lambda.js
|
|
62
|
+
const lambdaTemplate = `const { NavisApp } = require('navis.js');
|
|
63
|
+
|
|
64
|
+
const app = new NavisApp();
|
|
65
|
+
|
|
66
|
+
// Middleware
|
|
67
|
+
app.use((req, res, next) => {
|
|
68
|
+
console.log(\`Lambda: \${req.method} \${req.path}\`);
|
|
69
|
+
next();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Routes
|
|
73
|
+
app.get('/', (req, res) => {
|
|
74
|
+
res.statusCode = 200;
|
|
75
|
+
res.body = {
|
|
76
|
+
service: '${serviceName}',
|
|
77
|
+
message: 'Welcome to ${serviceName} service (Lambda)',
|
|
78
|
+
version: '1.0.0'
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
app.get('/health', (req, res) => {
|
|
83
|
+
res.statusCode = 200;
|
|
84
|
+
res.body = { status: 'ok', service: '${serviceName}' };
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Lambda handler
|
|
88
|
+
exports.handler = async (event) => {
|
|
89
|
+
return await app.handleLambda(event);
|
|
90
|
+
};
|
|
91
|
+
`;
|
|
92
|
+
|
|
93
|
+
fs.writeFileSync(path.join(serviceDir, 'lambda.js'), lambdaTemplate);
|
|
94
|
+
|
|
95
|
+
// Generate package.json
|
|
96
|
+
const packageJson = {
|
|
97
|
+
name: serviceName,
|
|
98
|
+
version: '1.0.0',
|
|
99
|
+
description: `${serviceName} microservice`,
|
|
100
|
+
main: 'service.js',
|
|
101
|
+
scripts: {
|
|
102
|
+
start: 'node service.js',
|
|
103
|
+
'start:lambda': 'node lambda.js',
|
|
104
|
+
},
|
|
105
|
+
dependencies: {
|
|
106
|
+
'navis.js': '^1.0.0',
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
fs.writeFileSync(
|
|
111
|
+
path.join(serviceDir, 'package.json'),
|
|
112
|
+
JSON.stringify(packageJson, null, 2)
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Generate README.md
|
|
116
|
+
const readmeTemplate = `# ${serviceName}
|
|
117
|
+
|
|
118
|
+
Microservice generated with Navis.js
|
|
119
|
+
|
|
120
|
+
## Installation
|
|
121
|
+
|
|
122
|
+
\`\`\`bash
|
|
123
|
+
npm install
|
|
124
|
+
\`\`\`
|
|
125
|
+
|
|
126
|
+
## Running
|
|
127
|
+
|
|
128
|
+
### Local Development
|
|
129
|
+
|
|
130
|
+
\`\`\`bash
|
|
131
|
+
npm start
|
|
132
|
+
# or
|
|
133
|
+
node service.js
|
|
134
|
+
\`\`\`
|
|
135
|
+
|
|
136
|
+
### AWS Lambda
|
|
137
|
+
|
|
138
|
+
Deploy \`lambda.js\` to AWS Lambda and configure API Gateway.
|
|
139
|
+
|
|
140
|
+
## API Endpoints
|
|
141
|
+
|
|
142
|
+
- \`GET /\` - Service information
|
|
143
|
+
- \`GET /health\` - Health check
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
Add your routes in \`service.js\`:
|
|
148
|
+
|
|
149
|
+
\`\`\`javascript
|
|
150
|
+
app.get('/api/users', (req, res) => {
|
|
151
|
+
// Your route handler
|
|
152
|
+
});
|
|
153
|
+
\`\`\`
|
|
154
|
+
`;
|
|
155
|
+
|
|
156
|
+
fs.writeFileSync(path.join(serviceDir, 'README.md'), readmeTemplate);
|
|
157
|
+
|
|
158
|
+
console.log(`✅ Service "${serviceName}" generated successfully!`);
|
|
159
|
+
console.log(`📁 Location: ${serviceDir}`);
|
|
160
|
+
console.log(`\nNext steps:`);
|
|
161
|
+
console.log(` cd ${serviceName}`);
|
|
162
|
+
console.log(` npm install`);
|
|
163
|
+
console.log(` npm start`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
module.exports = { generateService };
|
|
167
|
+
|
package/bin/navis.js
CHANGED
|
@@ -33,8 +33,57 @@ if (command === 'start') {
|
|
|
33
33
|
process.exit(code);
|
|
34
34
|
});
|
|
35
35
|
} else if (command === 'generate') {
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
const subcommand = process.argv[3];
|
|
37
|
+
|
|
38
|
+
if (subcommand === 'service') {
|
|
39
|
+
const serviceName = process.argv[4];
|
|
40
|
+
|
|
41
|
+
if (!serviceName) {
|
|
42
|
+
console.error('Error: Service name is required');
|
|
43
|
+
console.log('Usage: navis generate service <service-name>');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const { generateService } = require('./generators/service');
|
|
48
|
+
generateService(serviceName);
|
|
49
|
+
} else {
|
|
50
|
+
console.log('Generator commands:');
|
|
51
|
+
console.log(' navis generate service <name> Generate a new microservice');
|
|
52
|
+
console.log('');
|
|
53
|
+
console.log('Example:');
|
|
54
|
+
console.log(' navis generate service user-service');
|
|
55
|
+
}
|
|
56
|
+
} else if (command === 'deploy') {
|
|
57
|
+
// v3: Deploy command (placeholder for future deployment features)
|
|
58
|
+
console.log('Deploy command - Coming soon');
|
|
59
|
+
console.log('This will support deployment to AWS Lambda, Docker, etc.');
|
|
60
|
+
} else if (command === 'test') {
|
|
61
|
+
// v3: Test command
|
|
62
|
+
const { spawn } = require('child_process');
|
|
63
|
+
const testPath = path.join(__dirname, '..', 'verify-v2.js');
|
|
64
|
+
|
|
65
|
+
if (fs.existsSync(testPath)) {
|
|
66
|
+
const test = spawn('node', [testPath], {
|
|
67
|
+
stdio: 'inherit',
|
|
68
|
+
cwd: path.join(__dirname, '..'),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test.on('exit', (code) => {
|
|
72
|
+
process.exit(code);
|
|
73
|
+
});
|
|
74
|
+
} else {
|
|
75
|
+
console.log('No test file found');
|
|
76
|
+
}
|
|
77
|
+
} else if (command === 'metrics') {
|
|
78
|
+
// v3: Show metrics endpoint
|
|
79
|
+
console.log('Metrics endpoint:');
|
|
80
|
+
console.log(' Add /metrics route to your app to expose Prometheus metrics');
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log('Example:');
|
|
83
|
+
console.log(' app.get("/metrics", (req, res) => {');
|
|
84
|
+
console.log(' res.setHeader("Content-Type", "text/plain");');
|
|
85
|
+
console.log(' res.end(metrics.toPrometheus());');
|
|
86
|
+
console.log(' });');
|
|
38
87
|
} else {
|
|
39
88
|
console.log('Navis.js CLI');
|
|
40
89
|
console.log('');
|
|
@@ -43,4 +92,9 @@ if (command === 'start') {
|
|
|
43
92
|
console.log('');
|
|
44
93
|
console.log('v2 commands:');
|
|
45
94
|
console.log(' navis generate Generate service boilerplate');
|
|
95
|
+
console.log('');
|
|
96
|
+
console.log('v3 commands:');
|
|
97
|
+
console.log(' navis test Run verification tests');
|
|
98
|
+
console.log(' navis metrics Show metrics endpoint info');
|
|
99
|
+
console.log(' navis deploy Deploy service (coming soon)');
|
|
46
100
|
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navis.js v2 Features Demo
|
|
3
|
+
* Demonstrates retry logic, circuit breaker, service config, and service discovery
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
ServiceClient,
|
|
8
|
+
ServiceConfig,
|
|
9
|
+
ServiceDiscovery,
|
|
10
|
+
retry,
|
|
11
|
+
} = require('../src/index');
|
|
12
|
+
|
|
13
|
+
// Example 1: ServiceClient with retry and circuit breaker
|
|
14
|
+
async function demoServiceClient() {
|
|
15
|
+
console.log('\n=== ServiceClient with Retry & Circuit Breaker ===\n');
|
|
16
|
+
|
|
17
|
+
const client = new ServiceClient('http://localhost:3000', {
|
|
18
|
+
timeout: 3000,
|
|
19
|
+
maxRetries: 3,
|
|
20
|
+
retryBaseDelay: 1000,
|
|
21
|
+
circuitBreaker: {
|
|
22
|
+
failureThreshold: 3,
|
|
23
|
+
resetTimeout: 10000,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const response = await client.get('/health');
|
|
29
|
+
console.log('✅ Request successful:', response.data);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error.circuitBreakerOpen) {
|
|
32
|
+
console.log('❌ Circuit breaker is OPEN:', error.message);
|
|
33
|
+
console.log('Circuit state:', error.circuitState);
|
|
34
|
+
} else {
|
|
35
|
+
console.log('❌ Request failed:', error.message);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check circuit breaker state
|
|
40
|
+
const state = client.getCircuitBreakerState();
|
|
41
|
+
if (state) {
|
|
42
|
+
console.log('Circuit breaker state:', state);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Example 2: Service Configuration
|
|
47
|
+
async function demoServiceConfig() {
|
|
48
|
+
console.log('\n=== Service Configuration ===\n');
|
|
49
|
+
|
|
50
|
+
const config = new ServiceConfig({
|
|
51
|
+
defaultOptions: {
|
|
52
|
+
timeout: 5000,
|
|
53
|
+
retry: {
|
|
54
|
+
maxRetries: 3,
|
|
55
|
+
baseDelay: 1000,
|
|
56
|
+
},
|
|
57
|
+
circuitBreaker: {
|
|
58
|
+
failureThreshold: 5,
|
|
59
|
+
resetTimeout: 60000,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Register services
|
|
65
|
+
config.register('userService', 'http://localhost:3001', {
|
|
66
|
+
timeout: 3000,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
config.register('orderService', 'http://localhost:3002', {
|
|
70
|
+
retry: {
|
|
71
|
+
maxRetries: 5,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
console.log('Registered services:', Object.keys(config.getAll()));
|
|
76
|
+
|
|
77
|
+
// Get service config
|
|
78
|
+
const userServiceConfig = config.get('userService');
|
|
79
|
+
console.log('User service config:', userServiceConfig);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Example 3: Service Discovery
|
|
83
|
+
async function demoServiceDiscovery() {
|
|
84
|
+
console.log('\n=== Service Discovery ===\n');
|
|
85
|
+
|
|
86
|
+
const discovery = new ServiceDiscovery({
|
|
87
|
+
healthCheckInterval: 10000,
|
|
88
|
+
healthCheckPath: '/health',
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Register service with multiple endpoints
|
|
92
|
+
discovery.register('api', [
|
|
93
|
+
'http://localhost:3000',
|
|
94
|
+
'http://localhost:3001',
|
|
95
|
+
'http://localhost:3002',
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
// Get next available service (round-robin)
|
|
99
|
+
const url1 = discovery.getNext('api');
|
|
100
|
+
console.log('Next service URL:', url1);
|
|
101
|
+
|
|
102
|
+
const url2 = discovery.getNext('api');
|
|
103
|
+
console.log('Next service URL:', url2);
|
|
104
|
+
|
|
105
|
+
// Get all healthy services
|
|
106
|
+
const healthy = discovery.getHealthy('api');
|
|
107
|
+
console.log('Healthy services:', healthy);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Example 4: Using retry utility directly
|
|
111
|
+
async function demoRetryUtility() {
|
|
112
|
+
console.log('\n=== Retry Utility ===\n');
|
|
113
|
+
|
|
114
|
+
let attemptCount = 0;
|
|
115
|
+
|
|
116
|
+
const flakyFunction = async () => {
|
|
117
|
+
attemptCount++;
|
|
118
|
+
console.log(`Attempt ${attemptCount}...`);
|
|
119
|
+
|
|
120
|
+
if (attemptCount < 3) {
|
|
121
|
+
throw new Error('Temporary failure');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { success: true, attempt: attemptCount };
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const result = await retry(flakyFunction, {
|
|
129
|
+
maxRetries: 3,
|
|
130
|
+
baseDelay: 500,
|
|
131
|
+
});
|
|
132
|
+
console.log('✅ Success after retries:', result);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.log('❌ Failed after all retries:', error.message);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Example 5: ServiceClient with additional HTTP methods
|
|
139
|
+
async function demoHttpMethods() {
|
|
140
|
+
console.log('\n=== Additional HTTP Methods ===\n');
|
|
141
|
+
|
|
142
|
+
const client = new ServiceClient('http://localhost:3000', {
|
|
143
|
+
retry: false, // Disable retry for demo
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
// PUT request
|
|
148
|
+
const putResponse = await client.put('/api/users/1', { name: 'John' });
|
|
149
|
+
console.log('PUT response:', putResponse.data);
|
|
150
|
+
|
|
151
|
+
// DELETE request
|
|
152
|
+
const deleteResponse = await client.delete('/api/users/1');
|
|
153
|
+
console.log('DELETE response:', deleteResponse.data);
|
|
154
|
+
|
|
155
|
+
// PATCH request
|
|
156
|
+
const patchResponse = await client.patch('/api/users/1', { name: 'Jane' });
|
|
157
|
+
console.log('PATCH response:', patchResponse.data);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.log('Request failed (expected if server not running):', error.message);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Run all demos
|
|
164
|
+
async function runDemos() {
|
|
165
|
+
console.log('🚀 Navis.js v2 Features Demo\n');
|
|
166
|
+
console.log('='.repeat(50));
|
|
167
|
+
|
|
168
|
+
await demoServiceConfig();
|
|
169
|
+
await demoServiceDiscovery();
|
|
170
|
+
await demoRetryUtility();
|
|
171
|
+
await demoHttpMethods();
|
|
172
|
+
|
|
173
|
+
// ServiceClient demo requires a running server
|
|
174
|
+
console.log('\n💡 Note: ServiceClient demo requires a running server on port 3000');
|
|
175
|
+
console.log(' Run: node examples/server.js');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Run if called directly
|
|
179
|
+
if (require.main === module) {
|
|
180
|
+
runDemos().catch(console.error);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = {
|
|
184
|
+
demoServiceClient,
|
|
185
|
+
demoServiceConfig,
|
|
186
|
+
demoServiceDiscovery,
|
|
187
|
+
demoRetryUtility,
|
|
188
|
+
demoHttpMethods,
|
|
189
|
+
};
|
|
190
|
+
|