grpc-resilient 1.0.1 → 1.1.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/CHANGELOG.md +32 -14
- package/README.md +434 -15
- package/dist/gateway/GatewayCredentialsProvider.d.ts +68 -0
- package/dist/gateway/GatewayCredentialsProvider.d.ts.map +1 -0
- package/dist/gateway/GatewayCredentialsProvider.js +198 -0
- package/dist/gateway/GatewayCredentialsProvider.js.map +1 -0
- package/dist/gateway/GatewayGrpcClient.d.ts +134 -0
- package/dist/gateway/GatewayGrpcClient.d.ts.map +1 -0
- package/dist/gateway/GatewayGrpcClient.js +476 -0
- package/dist/gateway/GatewayGrpcClient.js.map +1 -0
- package/dist/gateway/GatewayMetricsTracker.d.ts +98 -0
- package/dist/gateway/GatewayMetricsTracker.d.ts.map +1 -0
- package/dist/gateway/GatewayMetricsTracker.js +135 -0
- package/dist/gateway/GatewayMetricsTracker.js.map +1 -0
- package/dist/gateway/GatewayRetryHandler.d.ts +85 -0
- package/dist/gateway/GatewayRetryHandler.d.ts.map +1 -0
- package/dist/gateway/GatewayRetryHandler.js +137 -0
- package/dist/gateway/GatewayRetryHandler.js.map +1 -0
- package/dist/gateway/index.d.ts +40 -0
- package/dist/gateway/index.d.ts.map +1 -0
- package/dist/gateway/index.js +43 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/gateway/types.d.ts +178 -0
- package/dist/gateway/types.d.ts.map +1 -0
- package/dist/gateway/types.js +53 -0
- package/dist/gateway/types.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,29 +1,47 @@
|
|
|
1
|
-
## [1.0
|
|
1
|
+
## [1.1.0](https://github.com/berkeerdo/grpc-resilient/compare/v1.0.1...v1.1.0) (2026-01-11)
|
|
2
2
|
|
|
3
|
-
###
|
|
3
|
+
### Features
|
|
4
4
|
|
|
5
|
-
*
|
|
5
|
+
* **gateway:** add gateway grpc client for api gateway support ([30b560e](https://github.com/berkeerdo/grpc-resilient/commit/30b560eb800f78f2929493a6b4825b91418ef6e4))
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
# Changelog
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
All notable changes to this project will be documented in this file.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
12
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
12
13
|
|
|
13
|
-
## 1.
|
|
14
|
+
## [1.1.0] - 2026-01-11
|
|
14
15
|
|
|
15
|
-
###
|
|
16
|
+
### Added
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
- **Gateway Client Module** - New `grpc-resilient/gateway` subpath export for API Gateways and BFF services
|
|
19
|
+
- `GatewayGrpcClient` - Abstract base class for gateway/proxy clients
|
|
20
|
+
- `GatewayMetricsTracker` - Metrics tracking for gateway clients
|
|
21
|
+
- `GatewayCredentialsProvider` - TLS/mTLS credential handling with security validation
|
|
22
|
+
- `GatewayRetryHandler` - Retry logic with exponential backoff and jitter
|
|
23
|
+
- Full TLS/mTLS support for gateway clients
|
|
24
|
+
- One-way TLS with system or custom CA
|
|
25
|
+
- Mutual TLS (mTLS) with client certificates
|
|
26
|
+
- Secure certificate path validation
|
|
27
|
+
- Additional gRPC metadata support for gateway calls
|
|
28
|
+
- Keepalive configuration options
|
|
29
|
+
- `validateTlsConfig()` utility for validating TLS configuration
|
|
30
|
+
- `getErrorDescription()` utility for human-readable error messages
|
|
31
|
+
- Comprehensive documentation with gateway examples
|
|
18
32
|
|
|
19
|
-
|
|
33
|
+
### Changed
|
|
20
34
|
|
|
21
|
-
|
|
35
|
+
- Updated package description to include API gateway support
|
|
36
|
+
- Added gateway-related keywords for npm discoverability
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
## [1.0.1] - 2026-01-11
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Peer dependency version constraints (`>=1.9.0` for grpc-js, `>=0.7.0` for proto-loader)
|
|
25
43
|
|
|
26
|
-
## [1.0.0] -
|
|
44
|
+
## [1.0.0] - 2026-01-11
|
|
27
45
|
|
|
28
46
|
### Added
|
|
29
47
|
|
package/README.md
CHANGED
|
@@ -5,18 +5,22 @@
|
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
7
|
|
|
8
|
-
Production-ready gRPC client for Node.js with built-in resilience patterns.
|
|
8
|
+
Production-ready gRPC client for Node.js with built-in resilience patterns. Perfect for microservices and API gateways.
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
|
|
12
12
|
- **Lazy Connection** - Connects on first use, not at startup
|
|
13
13
|
- **Auto-Reconnect** - Exponential backoff reconnection strategy
|
|
14
|
-
- **Retry Logic** - Configurable retry with
|
|
14
|
+
- **Retry Logic** - Configurable retry with jitter to prevent thundering herd
|
|
15
15
|
- **Fallback Cache** - Graceful degradation when service is unavailable
|
|
16
16
|
- **Metrics** - OpenTelemetry-compatible metrics tracking
|
|
17
17
|
- **Health Checks** - Built-in health status reporting
|
|
18
|
+
- **TLS/mTLS** - Full TLS support including mutual TLS
|
|
18
19
|
- **TypeScript** - Full type safety with generics support
|
|
19
20
|
- **ESM** - Native ES modules support
|
|
21
|
+
- **Two Client Types**:
|
|
22
|
+
- `ResilientGrpcClient` - For microservice-to-microservice communication
|
|
23
|
+
- `GatewayGrpcClient` - For API Gateways and BFF services
|
|
20
24
|
|
|
21
25
|
## Installation
|
|
22
26
|
|
|
@@ -24,7 +28,23 @@ Production-ready gRPC client for Node.js with built-in resilience patterns.
|
|
|
24
28
|
npm install grpc-resilient @grpc/grpc-js @grpc/proto-loader
|
|
25
29
|
```
|
|
26
30
|
|
|
27
|
-
##
|
|
31
|
+
## Table of Contents
|
|
32
|
+
|
|
33
|
+
- [Microservice Client](#microservice-client) - Service-to-service communication
|
|
34
|
+
- [Gateway Client](#gateway-client) - API Gateway / Proxy pattern
|
|
35
|
+
- [Configuration](#configuration)
|
|
36
|
+
- [Health & Metrics](#health--metrics)
|
|
37
|
+
- [Events](#events)
|
|
38
|
+
- [TLS Configuration](#tls-configuration)
|
|
39
|
+
- [Best Practices](#best-practices)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Microservice Client
|
|
44
|
+
|
|
45
|
+
Use `ResilientGrpcClient` for backend service-to-service communication with fallback cache support.
|
|
46
|
+
|
|
47
|
+
### Quick Start
|
|
28
48
|
|
|
29
49
|
```typescript
|
|
30
50
|
import { ResilientGrpcClient, type GrpcLogger } from 'grpc-resilient';
|
|
@@ -74,8 +94,200 @@ const client = UserServiceClient.getInstance('localhost:50051', logger);
|
|
|
74
94
|
const user = await client.getUser('123');
|
|
75
95
|
```
|
|
76
96
|
|
|
97
|
+
### Fallback Cache
|
|
98
|
+
|
|
99
|
+
Enable graceful degradation when the backend service is temporarily unavailable:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
class UserServiceClient extends ResilientGrpcClient {
|
|
103
|
+
constructor(grpcUrl: string, logger: GrpcLogger) {
|
|
104
|
+
super({
|
|
105
|
+
serviceName: 'UserService',
|
|
106
|
+
grpcUrl,
|
|
107
|
+
protoFile: 'user.proto',
|
|
108
|
+
packageName: 'myapp.user',
|
|
109
|
+
serviceClassName: 'UserService',
|
|
110
|
+
protosPath: fileURLToPath(new URL('./protos', import.meta.url)),
|
|
111
|
+
logger,
|
|
112
|
+
// Fallback cache configuration
|
|
113
|
+
enableFallbackCache: true,
|
|
114
|
+
fallbackCacheTtlMs: 60000, // Cache for 1 minute
|
|
115
|
+
maxCacheSize: 100, // Max 100 entries
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async getUser(userId: string) {
|
|
120
|
+
return this.call('GetUser', { userId }, {
|
|
121
|
+
cacheKey: `user:${userId}`, // Custom cache key
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Call Options
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Per-call options
|
|
131
|
+
const user = await client.call('GetUser', { userId: '123' }, {
|
|
132
|
+
timeoutMs: 10000, // Override timeout
|
|
133
|
+
locale: 'en-US', // Send locale in metadata
|
|
134
|
+
skipRetry: true, // Disable retry for this call
|
|
135
|
+
cacheKey: 'user:123', // Custom cache key
|
|
136
|
+
skipCache: false, // Use cache if available
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Gateway Client
|
|
143
|
+
|
|
144
|
+
Use `GatewayGrpcClient` for API Gateways, BFF (Backend for Frontend) services, or reverse proxies.
|
|
145
|
+
|
|
146
|
+
### Quick Start
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import {
|
|
150
|
+
GatewayGrpcClient,
|
|
151
|
+
type GatewayClientConfig,
|
|
152
|
+
type GatewayCallOptions,
|
|
153
|
+
type GatewayLogger,
|
|
154
|
+
} from 'grpc-resilient/gateway';
|
|
155
|
+
import * as grpc from '@grpc/grpc-js';
|
|
156
|
+
|
|
157
|
+
// Define your typed client interface (optional but recommended)
|
|
158
|
+
interface AuthClient extends grpc.Client {
|
|
159
|
+
ValidateToken: grpc.MethodDefinition<ValidateRequest, ValidateResponse>;
|
|
160
|
+
RefreshToken: grpc.MethodDefinition<RefreshRequest, RefreshResponse>;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
class AuthServiceProxy extends GatewayGrpcClient<AuthClient> {
|
|
164
|
+
private static instance: AuthServiceProxy | null = null;
|
|
165
|
+
|
|
166
|
+
private constructor(config: GatewayClientConfig, logger: GatewayLogger) {
|
|
167
|
+
super(config, logger);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
static getInstance(grpcUrl: string, logger: GatewayLogger): AuthServiceProxy {
|
|
171
|
+
if (!AuthServiceProxy.instance) {
|
|
172
|
+
AuthServiceProxy.instance = new AuthServiceProxy(
|
|
173
|
+
{
|
|
174
|
+
serviceName: 'AuthService',
|
|
175
|
+
grpcUrl,
|
|
176
|
+
protoFile: 'auth.proto',
|
|
177
|
+
packageName: 'auth',
|
|
178
|
+
serviceClassName: 'AuthService',
|
|
179
|
+
protosPath: '/app/protos',
|
|
180
|
+
timeoutMs: 5000,
|
|
181
|
+
retryCount: 2,
|
|
182
|
+
},
|
|
183
|
+
logger
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return AuthServiceProxy.instance;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async validateToken(token: string, options?: GatewayCallOptions) {
|
|
190
|
+
return this.callWithRetry<ValidateRequest, ValidateResponse>(
|
|
191
|
+
'ValidateToken',
|
|
192
|
+
{ token },
|
|
193
|
+
options
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async refreshToken(refreshToken: string, options?: GatewayCallOptions) {
|
|
198
|
+
return this.callWithRetry<RefreshRequest, RefreshResponse>(
|
|
199
|
+
'RefreshToken',
|
|
200
|
+
{ refreshToken },
|
|
201
|
+
options
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Usage in Express/Fastify route handler
|
|
207
|
+
app.post('/api/auth/validate', async (req, res) => {
|
|
208
|
+
const client = AuthServiceProxy.getInstance(process.env.AUTH_SERVICE_URL, logger);
|
|
209
|
+
|
|
210
|
+
const result = await client.validateToken(req.headers.authorization, {
|
|
211
|
+
locale: req.headers['accept-language'],
|
|
212
|
+
clientUrl: req.headers['x-forwarded-for'],
|
|
213
|
+
metadata: {
|
|
214
|
+
'x-request-id': req.id,
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
res.json(result);
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Multiple Backend Services
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Gateway managing multiple backend services
|
|
226
|
+
class GatewayClients {
|
|
227
|
+
private static authClient: AuthServiceProxy;
|
|
228
|
+
private static userClient: UserServiceProxy;
|
|
229
|
+
private static orderClient: OrderServiceProxy;
|
|
230
|
+
|
|
231
|
+
static getAuthClient(logger: GatewayLogger): AuthServiceProxy {
|
|
232
|
+
if (!this.authClient) {
|
|
233
|
+
this.authClient = AuthServiceProxy.getInstance(
|
|
234
|
+
process.env.AUTH_SERVICE_URL!,
|
|
235
|
+
logger
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
return this.authClient;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
static getUserClient(logger: GatewayLogger): UserServiceProxy {
|
|
242
|
+
if (!this.userClient) {
|
|
243
|
+
this.userClient = UserServiceProxy.getInstance(
|
|
244
|
+
process.env.USER_SERVICE_URL!,
|
|
245
|
+
logger
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
return this.userClient;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
static getOrderClient(logger: GatewayLogger): OrderServiceProxy {
|
|
252
|
+
if (!this.orderClient) {
|
|
253
|
+
this.orderClient = OrderServiceProxy.getInstance(
|
|
254
|
+
process.env.ORDER_SERVICE_URL!,
|
|
255
|
+
logger
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
return this.orderClient;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Graceful shutdown
|
|
262
|
+
static closeAll(): void {
|
|
263
|
+
this.authClient?.close();
|
|
264
|
+
this.userClient?.close();
|
|
265
|
+
this.orderClient?.close();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Gateway Call Options
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
const result = await client.callWithRetry('GetUser', request, {
|
|
274
|
+
timeoutMs: 10000, // Override timeout
|
|
275
|
+
locale: 'tr-TR', // Forwarded to backend via accept-language header
|
|
276
|
+
clientUrl: '192.168.1.1', // Client IP for logging (x-client-url header)
|
|
277
|
+
skipRetry: false, // Enable/disable retry
|
|
278
|
+
metadata: { // Additional gRPC metadata
|
|
279
|
+
'x-request-id': 'abc123',
|
|
280
|
+
'x-correlation-id': 'xyz789',
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
77
287
|
## Configuration
|
|
78
288
|
|
|
289
|
+
### Microservice Client Options
|
|
290
|
+
|
|
79
291
|
| Option | Type | Default | Description |
|
|
80
292
|
|--------|------|---------|-------------|
|
|
81
293
|
| `serviceName` | string | **required** | Name for logging and metrics |
|
|
@@ -96,8 +308,35 @@ const user = await client.getUser('123');
|
|
|
96
308
|
| `fallbackCacheTtlMs` | number | 60000 | Cache TTL in milliseconds |
|
|
97
309
|
| `maxCacheSize` | number | 100 | Maximum cache entries |
|
|
98
310
|
|
|
311
|
+
### Gateway Client Options
|
|
312
|
+
|
|
313
|
+
| Option | Type | Default | Description |
|
|
314
|
+
|--------|------|---------|-------------|
|
|
315
|
+
| `serviceName` | string | **required** | Name for logging and metrics |
|
|
316
|
+
| `grpcUrl` | string | **required** | gRPC server URL (host:port) |
|
|
317
|
+
| `protoFile` | string | **required** | Proto file name |
|
|
318
|
+
| `packageName` | string | **required** | Package name in proto file |
|
|
319
|
+
| `serviceClassName` | string | **required** | Service class name in proto |
|
|
320
|
+
| `protosPath` | string | **required** | Absolute path to protos directory |
|
|
321
|
+
| `timeoutMs` | number | 5000 | Call timeout in milliseconds |
|
|
322
|
+
| `retryCount` | number | 3 | Number of retry attempts |
|
|
323
|
+
| `retryDelayMs` | number | 1000 | Base delay between retries |
|
|
324
|
+
| `maxReconnectAttempts` | number | Infinity | Max reconnection attempts |
|
|
325
|
+
| `maxReconnectDelayMs` | number | 30000 | Max reconnection delay |
|
|
326
|
+
| `initialReconnectDelayMs` | number | 1000 | Initial reconnection delay |
|
|
327
|
+
| `useTls` | boolean | false | Use TLS for connection |
|
|
328
|
+
| `tlsCaCertPath` | string | undefined | CA certificate path |
|
|
329
|
+
| `tlsClientCertPath` | string | undefined | Client cert path (mTLS) |
|
|
330
|
+
| `tlsClientKeyPath` | string | undefined | Client key path (mTLS) |
|
|
331
|
+
| `keepaliveTimeMs` | number | 30000 | Keepalive interval |
|
|
332
|
+
| `keepaliveTimeoutMs` | number | 10000 | Keepalive timeout |
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
99
336
|
## Health & Metrics
|
|
100
337
|
|
|
338
|
+
Both client types provide health and metrics APIs:
|
|
339
|
+
|
|
101
340
|
```typescript
|
|
102
341
|
// Get health status
|
|
103
342
|
const health = client.getHealth();
|
|
@@ -112,7 +351,7 @@ console.log(health);
|
|
|
112
351
|
// metrics: { ... }
|
|
113
352
|
// }
|
|
114
353
|
|
|
115
|
-
// Get metrics for OpenTelemetry
|
|
354
|
+
// Get metrics for OpenTelemetry / Prometheus
|
|
116
355
|
const metrics = client.getMetrics();
|
|
117
356
|
// {
|
|
118
357
|
// totalCalls: 150,
|
|
@@ -120,15 +359,45 @@ const metrics = client.getMetrics();
|
|
|
120
359
|
// failedCalls: 2,
|
|
121
360
|
// totalRetries: 5,
|
|
122
361
|
// avgLatencyMs: 42,
|
|
123
|
-
//
|
|
362
|
+
// minLatencyMs: 12,
|
|
363
|
+
// maxLatencyMs: 234,
|
|
364
|
+
// lastResetAt: Date
|
|
124
365
|
// }
|
|
125
366
|
|
|
126
|
-
//
|
|
367
|
+
// Check connection status
|
|
368
|
+
if (client.isConnected()) {
|
|
369
|
+
// Safe to make calls
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Reset metrics (useful for periodic collection)
|
|
127
373
|
client.resetMetrics();
|
|
128
374
|
```
|
|
129
375
|
|
|
376
|
+
### Health Check Endpoint Example
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
app.get('/health/grpc', (req, res) => {
|
|
380
|
+
const clients = {
|
|
381
|
+
auth: GatewayClients.getAuthClient(logger).getHealth(),
|
|
382
|
+
user: GatewayClients.getUserClient(logger).getHealth(),
|
|
383
|
+
order: GatewayClients.getOrderClient(logger).getHealth(),
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const allHealthy = Object.values(clients).every(c => c.healthy);
|
|
387
|
+
|
|
388
|
+
res.status(allHealthy ? 200 : 503).json({
|
|
389
|
+
status: allHealthy ? 'healthy' : 'degraded',
|
|
390
|
+
services: clients,
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
130
397
|
## Events
|
|
131
398
|
|
|
399
|
+
Both client types emit events for connection lifecycle:
|
|
400
|
+
|
|
132
401
|
```typescript
|
|
133
402
|
client.on('connected', () => {
|
|
134
403
|
console.log('gRPC client connected');
|
|
@@ -147,19 +416,140 @@ client.on('connecting', () => {
|
|
|
147
416
|
});
|
|
148
417
|
```
|
|
149
418
|
|
|
150
|
-
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## TLS Configuration
|
|
422
|
+
|
|
423
|
+
### One-way TLS (Server Verification)
|
|
151
424
|
|
|
152
425
|
```typescript
|
|
153
|
-
//
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
426
|
+
// Using system CA certificates
|
|
427
|
+
const client = new MyServiceClient({
|
|
428
|
+
serviceName: 'MyService',
|
|
429
|
+
grpcUrl: 'myservice.example.com:443',
|
|
430
|
+
protoFile: 'service.proto',
|
|
431
|
+
packageName: 'myapp',
|
|
432
|
+
serviceClassName: 'MyService',
|
|
433
|
+
protosPath: '/app/protos',
|
|
434
|
+
useTls: true, // Uses system CA
|
|
435
|
+
}, logger);
|
|
436
|
+
|
|
437
|
+
// Using custom CA certificate
|
|
438
|
+
const client = new MyServiceClient({
|
|
439
|
+
...config,
|
|
440
|
+
useTls: true,
|
|
441
|
+
tlsCaCertPath: '/etc/certs/ca.pem',
|
|
442
|
+
}, logger);
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Mutual TLS (mTLS)
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
const client = new MyServiceClient({
|
|
449
|
+
serviceName: 'MyService',
|
|
450
|
+
grpcUrl: 'myservice.example.com:443',
|
|
451
|
+
protoFile: 'service.proto',
|
|
452
|
+
packageName: 'myapp',
|
|
453
|
+
serviceClassName: 'MyService',
|
|
454
|
+
protosPath: '/app/protos',
|
|
455
|
+
useTls: true,
|
|
456
|
+
tlsCaCertPath: '/etc/certs/ca.pem',
|
|
457
|
+
tlsClientCertPath: '/etc/certs/client.pem',
|
|
458
|
+
tlsClientKeyPath: '/etc/certs/client.key',
|
|
459
|
+
}, logger);
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Validate TLS Configuration
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
import { validateTlsConfig } from 'grpc-resilient/gateway';
|
|
466
|
+
|
|
467
|
+
const validation = validateTlsConfig({
|
|
468
|
+
useTls: true,
|
|
469
|
+
caCertPath: '/etc/certs/ca.pem',
|
|
470
|
+
clientCertPath: '/etc/certs/client.pem',
|
|
471
|
+
clientKeyPath: '/etc/certs/client.key',
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
if (!validation.valid) {
|
|
475
|
+
console.error('TLS config error:', validation.error);
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Best Practices
|
|
482
|
+
|
|
483
|
+
### 1. Use Singleton Pattern
|
|
484
|
+
|
|
485
|
+
Always use singleton pattern to reuse connections:
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
class MyServiceClient extends ResilientGrpcClient {
|
|
489
|
+
private static instance: MyServiceClient | null = null;
|
|
490
|
+
|
|
491
|
+
private constructor(grpcUrl: string, logger: GrpcLogger) {
|
|
492
|
+
super({ ... });
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
static getInstance(grpcUrl: string, logger: GrpcLogger): MyServiceClient {
|
|
496
|
+
if (!MyServiceClient.instance) {
|
|
497
|
+
MyServiceClient.instance = new MyServiceClient(grpcUrl, logger);
|
|
498
|
+
}
|
|
499
|
+
return MyServiceClient.instance;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### 2. Graceful Shutdown
|
|
505
|
+
|
|
506
|
+
Close clients during application shutdown:
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
process.on('SIGTERM', () => {
|
|
510
|
+
client.close();
|
|
511
|
+
process.exit(0);
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### 3. Configure Timeouts Appropriately
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
// Short timeout for auth/validation calls
|
|
519
|
+
const authClient = new AuthServiceClient({
|
|
520
|
+
...config,
|
|
521
|
+
timeoutMs: 2000, // 2 seconds
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
// Longer timeout for data-heavy operations
|
|
525
|
+
const reportClient = new ReportServiceClient({
|
|
526
|
+
...config,
|
|
527
|
+
timeoutMs: 30000, // 30 seconds
|
|
160
528
|
});
|
|
161
529
|
```
|
|
162
530
|
|
|
531
|
+
### 4. Use Type-Safe Clients
|
|
532
|
+
|
|
533
|
+
Define request/response types for better type safety:
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
interface GetUserRequest {
|
|
537
|
+
userId: string;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
interface GetUserResponse {
|
|
541
|
+
id: string;
|
|
542
|
+
name: string;
|
|
543
|
+
email: string;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
async getUser(userId: string): Promise<GetUserResponse> {
|
|
547
|
+
return this.call<GetUserRequest, GetUserResponse>('GetUser', { userId });
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
163
553
|
## Logger Interface
|
|
164
554
|
|
|
165
555
|
Any logger that implements this interface works:
|
|
@@ -172,9 +562,38 @@ interface GrpcLogger {
|
|
|
172
562
|
debug(obj: object, msg?: string): void;
|
|
173
563
|
}
|
|
174
564
|
|
|
175
|
-
//
|
|
565
|
+
// Compatible loggers: pino, winston, bunyan, console
|
|
176
566
|
```
|
|
177
567
|
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## API Reference
|
|
571
|
+
|
|
572
|
+
### ResilientGrpcClient (Microservice)
|
|
573
|
+
|
|
574
|
+
| Method | Description |
|
|
575
|
+
|--------|-------------|
|
|
576
|
+
| `call<TReq, TRes>(method, request, options?)` | Make a gRPC call with retry |
|
|
577
|
+
| `getHealth()` | Get health status with metrics |
|
|
578
|
+
| `getMetrics()` | Get metrics only |
|
|
579
|
+
| `resetMetrics()` | Reset all metrics |
|
|
580
|
+
| `isConnected()` | Check connection status |
|
|
581
|
+
| `close()` | Close connection |
|
|
582
|
+
|
|
583
|
+
### GatewayGrpcClient (Gateway)
|
|
584
|
+
|
|
585
|
+
| Method | Description |
|
|
586
|
+
|--------|-------------|
|
|
587
|
+
| `callWithRetry<TReq, TRes>(method, request, options?)` | Make a gRPC call with retry |
|
|
588
|
+
| `ensureConnected()` | Ensure connection is established |
|
|
589
|
+
| `getHealth()` | Get health status with metrics |
|
|
590
|
+
| `getMetrics()` | Get metrics only |
|
|
591
|
+
| `resetMetrics()` | Reset all metrics |
|
|
592
|
+
| `isConnected()` | Check connection status |
|
|
593
|
+
| `close()` | Close connection |
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
178
597
|
## Requirements
|
|
179
598
|
|
|
180
599
|
- Node.js >= 18.0.0
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway gRPC Credentials Provider
|
|
3
|
+
*
|
|
4
|
+
* Handles TLS/SSL credentials for gRPC connections in API Gateway scenarios.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - One-way TLS (server verification only)
|
|
8
|
+
* - Mutual TLS (mTLS) with client certificates
|
|
9
|
+
* - System CA or custom CA certificates
|
|
10
|
+
* - Secure certificate path validation
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // Insecure (development only)
|
|
15
|
+
* const creds = createGatewayCredentials({
|
|
16
|
+
* serviceName: 'AuthService',
|
|
17
|
+
* useTls: false,
|
|
18
|
+
* logger,
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // One-way TLS with system CA
|
|
22
|
+
* const creds = createGatewayCredentials({
|
|
23
|
+
* serviceName: 'AuthService',
|
|
24
|
+
* useTls: true,
|
|
25
|
+
* logger,
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Mutual TLS with custom CA
|
|
29
|
+
* const creds = createGatewayCredentials({
|
|
30
|
+
* serviceName: 'AuthService',
|
|
31
|
+
* useTls: true,
|
|
32
|
+
* caCertPath: '/etc/certs/ca.pem',
|
|
33
|
+
* clientCertPath: '/etc/certs/client.pem',
|
|
34
|
+
* clientKeyPath: '/etc/certs/client.key',
|
|
35
|
+
* logger,
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @packageDocumentation
|
|
40
|
+
*/
|
|
41
|
+
import * as grpc from '@grpc/grpc-js';
|
|
42
|
+
import type { TlsCredentialsOptions } from './types.js';
|
|
43
|
+
/**
|
|
44
|
+
* Create gRPC client credentials for gateway connections
|
|
45
|
+
*
|
|
46
|
+
* Supports:
|
|
47
|
+
* - Insecure connections (development only)
|
|
48
|
+
* - One-way TLS with system CA
|
|
49
|
+
* - One-way TLS with custom CA
|
|
50
|
+
* - Mutual TLS (mTLS)
|
|
51
|
+
*
|
|
52
|
+
* @param options - Credential configuration options
|
|
53
|
+
* @returns gRPC channel credentials
|
|
54
|
+
*/
|
|
55
|
+
export declare function createGatewayCredentials(options: TlsCredentialsOptions): grpc.ChannelCredentials;
|
|
56
|
+
/**
|
|
57
|
+
* Check if a TLS configuration is valid
|
|
58
|
+
*
|
|
59
|
+
* Useful for validating configuration before creating clients.
|
|
60
|
+
*
|
|
61
|
+
* @param options - TLS configuration to validate
|
|
62
|
+
* @returns Object with validation result and any error message
|
|
63
|
+
*/
|
|
64
|
+
export declare function validateTlsConfig(options: Pick<TlsCredentialsOptions, 'useTls' | 'caCertPath' | 'clientCertPath' | 'clientKeyPath'>): {
|
|
65
|
+
valid: boolean;
|
|
66
|
+
error?: string;
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=GatewayCredentialsProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GatewayCredentialsProvider.d.ts","sourceRoot":"","sources":["../../src/gateway/GatewayCredentialsProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AAGtC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AA6DxD;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAAC,kBAAkB,CAShG;AA+CD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,GAAG,YAAY,GAAG,gBAAgB,GAAG,eAAe,CAAC,GACjG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA+CpC"}
|