opticore-api-gateway 1.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 +618 -0
- package/dist/index.cjs +1100 -0
- package/dist/index.d.cts +329 -0
- package/dist/index.d.ts +329 -0
- package/dist/index.js +1056 -0
- package/dist/utils/translations/message.translation.en.json +5 -0
- package/dist/utils/translations/message.translation.fr.json +5 -0
- package/package.json +50 -0
- package/scripts/postinstall.cjs +127 -0
package/README.md
ADDED
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
# opticore-api-gateway
|
|
2
|
+
|
|
3
|
+
A flexible, production-ready API Gateway built in TypeScript, powered by the [Opticore](https://github.com/guyzoum77) ecosystem. It provides dynamic routing, load balancing, middleware chaining, circuit breaking, and service registry out of the box.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Features](#features)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Quick Start — Standalone mode](#quick-start--standalone-mode)
|
|
12
|
+
- [Integration with opticore-webapp](#integration-with-opticore-webapp)
|
|
13
|
+
- [Configuration](#configuration)
|
|
14
|
+
- [`IGatewayConfig`](#igatewayconfig-interface)
|
|
15
|
+
- [`IRouteConfig`](#irouteconfig-interface)
|
|
16
|
+
- [`IServiceConfig`](#iserviceconfig-interface)
|
|
17
|
+
- [`ICircuitBreakerConfig`](#icircuitbreakerconfig-interface)
|
|
18
|
+
- [Load Balancing Strategies](#load-balancing-strategies)
|
|
19
|
+
- [Middleware](#middleware)
|
|
20
|
+
- [Global Middlewares](#global-middlewares)
|
|
21
|
+
- [Route-Level Middlewares](#route-level-middlewares)
|
|
22
|
+
- [Built-in Middlewares](#built-in-middlewares)
|
|
23
|
+
- [Creating Custom Middlewares](#creating-custom-middlewares)
|
|
24
|
+
- [API Reference](#api-reference)
|
|
25
|
+
- [Types & Interfaces](#types--interfaces)
|
|
26
|
+
- [Advanced Usage](#advanced-usage)
|
|
27
|
+
- [Error Handling](#error-handling)
|
|
28
|
+
- [License](#license)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- **Dynamic Routing** — Define routes with HTTP method, path, and target service(s)
|
|
35
|
+
- **Load Balancing** — Four strategies: `round-robin`, `weighted`, `random`, `least-connections`
|
|
36
|
+
- **Service Registry** — Automatic service health tracking and instance management
|
|
37
|
+
- **Circuit Breaker** — Per-route circuit breaker with configurable thresholds and reset timeouts
|
|
38
|
+
- **Middleware Chain** — Global and per-route middleware support (Express-compatible signature)
|
|
39
|
+
- **Request Forwarding** — Transparent proxying with header rewriting and tracing headers
|
|
40
|
+
- **Logging** — Built-in `LoggingMiddleware` with timing and status code
|
|
41
|
+
- **Validation** — Built-in `ValidationMiddleware` using `opticore-validator`
|
|
42
|
+
- **opticore-webapp Integration** — Drop-in replacement for `registerRouter()` in any Opticore template project
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
**Request lifecycle:**
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Incoming request
|
|
50
|
+
│
|
|
51
|
+
▼
|
|
52
|
+
Global middlewares (auth, rate-limit, logging, …)
|
|
53
|
+
│
|
|
54
|
+
▼
|
|
55
|
+
Route matched by method + path prefix
|
|
56
|
+
│
|
|
57
|
+
▼
|
|
58
|
+
Route-level middlewares
|
|
59
|
+
│
|
|
60
|
+
▼
|
|
61
|
+
Load balancer selects a healthy service instance
|
|
62
|
+
│
|
|
63
|
+
▼
|
|
64
|
+
Request forwarded to target with tracing headers
|
|
65
|
+
│
|
|
66
|
+
▼
|
|
67
|
+
Response sent back to client
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm install opticore-api-gateway
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
> Requires Node.js >= 16 and TypeScript >= 5.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Quick Start — Standalone mode
|
|
83
|
+
|
|
84
|
+
The gateway creates its own HTTP server and proxies requests to backend services.
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import {
|
|
88
|
+
APIGateway,
|
|
89
|
+
AuthMiddleware,
|
|
90
|
+
RateLimitMiddleware,
|
|
91
|
+
LoggingMiddleware
|
|
92
|
+
} from 'opticore-api-gateway';
|
|
93
|
+
|
|
94
|
+
const gateway = new APIGateway({
|
|
95
|
+
port: 3000,
|
|
96
|
+
loadBalancer: 'round-robin',
|
|
97
|
+
enableLogging: true,
|
|
98
|
+
|
|
99
|
+
services: [
|
|
100
|
+
{
|
|
101
|
+
name: 'user-service',
|
|
102
|
+
url: 'http://localhost:4001',
|
|
103
|
+
healthCheck: '/health',
|
|
104
|
+
timeout: 5000,
|
|
105
|
+
retries: 3,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'order-service',
|
|
109
|
+
url: 'http://localhost:4002',
|
|
110
|
+
healthCheck: '/health',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
|
|
114
|
+
globalMiddlewares: [
|
|
115
|
+
new LoggingMiddleware('info'),
|
|
116
|
+
new RateLimitMiddleware(100, 60000),
|
|
117
|
+
],
|
|
118
|
+
|
|
119
|
+
routes: [
|
|
120
|
+
{
|
|
121
|
+
path: '/users',
|
|
122
|
+
method: 'get',
|
|
123
|
+
target: 'http://localhost:4001',
|
|
124
|
+
serviceName: 'user-service',
|
|
125
|
+
timeout: 5000,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
path: '/orders',
|
|
129
|
+
method: 'post',
|
|
130
|
+
target: 'http://localhost:4002',
|
|
131
|
+
serviceName: 'order-service',
|
|
132
|
+
middlewares: [new AuthMiddleware(['my-secret-key'])],
|
|
133
|
+
circuitBreaker: {
|
|
134
|
+
failureThreshold: 5,
|
|
135
|
+
resetTimeout: 30000,
|
|
136
|
+
halfOpenMaxAttempts: 2,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
await gateway.start();
|
|
143
|
+
// → API Gateway running on port 3000
|
|
144
|
+
// → Registered 2 routes
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Integration with opticore-webapp
|
|
150
|
+
|
|
151
|
+
When you install `opticore-api-gateway` in an Opticore template project (e.g. [`opticore-api-restfull-template-mysql`](https://github.com/guyzoum77/opticore-api-restfull-template-mysql)), the gateway can **replace** the `registerRouter()` call and manage all routing, middleware, and proxying.
|
|
152
|
+
|
|
153
|
+
### How it works
|
|
154
|
+
|
|
155
|
+
`gateway.getOpticoreRoutes()` returns a `TFeatureRoutes[]` array that is directly passed to `WebServerCore.onStartServer()`. Each gateway route is mounted on the Express app with its global middlewares already embedded.
|
|
156
|
+
|
|
157
|
+
### Step-by-step integration
|
|
158
|
+
|
|
159
|
+
**1. Install the package**
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npm install opticore-api-gateway
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**2. Replace `registerRouter()` in `src/bootstrap/server/webApp.server.ts`**
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import {
|
|
169
|
+
APIGateway,
|
|
170
|
+
AuthMiddleware,
|
|
171
|
+
RateLimitMiddleware,
|
|
172
|
+
LoggingMiddleware
|
|
173
|
+
} from 'opticore-api-gateway';
|
|
174
|
+
|
|
175
|
+
const gateway = new APIGateway({
|
|
176
|
+
port: 4200, // informational — Express port is managed by WebServerCore
|
|
177
|
+
services: [
|
|
178
|
+
{ name: 'user-service', url: 'http://user-service:4001' },
|
|
179
|
+
{ name: 'product-service', url: 'http://product-service:4002' },
|
|
180
|
+
],
|
|
181
|
+
routes: [
|
|
182
|
+
{
|
|
183
|
+
path: '/api/users',
|
|
184
|
+
method: 'get',
|
|
185
|
+
target: 'http://user-service:4001',
|
|
186
|
+
serviceName: 'user-service',
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
path: '/api/products',
|
|
190
|
+
method: 'get',
|
|
191
|
+
// Multiple targets → load balancing is activated
|
|
192
|
+
target: ['http://product-service-1:4002', 'http://product-service-2:4002'],
|
|
193
|
+
serviceName: 'product-service',
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
globalMiddlewares: [
|
|
197
|
+
new LoggingMiddleware('info'),
|
|
198
|
+
new RateLimitMiddleware(100, 60000),
|
|
199
|
+
new AuthMiddleware(['my-api-key']),
|
|
200
|
+
],
|
|
201
|
+
loadBalancer: 'round-robin',
|
|
202
|
+
enableLogging: true,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Pass gateway routes instead of registerRouter()
|
|
206
|
+
webApp.onStartServer(gateway.getOpticoreRoutes(), dbConnection, dependenciesProvider);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**3. (Optional) Apply gateway middlewares globally via Express**
|
|
210
|
+
|
|
211
|
+
If you want gateway middlewares to also run on routes outside the gateway (e.g. static files, error pages), use `getExpressMiddleware()`:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// Before calling onStartServer:
|
|
215
|
+
expressApp.use(gateway.getExpressMiddleware());
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### What `getOpticoreRoutes()` does internally
|
|
219
|
+
|
|
220
|
+
- Creates one Express Router per configured route, mounted at `config.path`
|
|
221
|
+
- Each router handles the `*` wildcard — so `/api/users/123`, `/api/users?page=2` etc. are all captured
|
|
222
|
+
- The handler restores `req.url` to `req.originalUrl` (full path) before proxying so URL rewriting is correct
|
|
223
|
+
- Global middlewares run before the proxy forward for every matched request
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Configuration
|
|
228
|
+
|
|
229
|
+
### `IGatewayConfig` interface
|
|
230
|
+
|
|
231
|
+
| Property | Type | Required | Description |
|
|
232
|
+
|---------------------|--------------------------|----------|-----------------------------------------------|
|
|
233
|
+
| `port` | `number` | ✅ | Port used by `start()` (standalone mode) |
|
|
234
|
+
| `services` | `IServiceConfig[]` | ✅ | Backend services to register |
|
|
235
|
+
| `routes` | `IRouteConfig[]` | ✅ | Route definitions |
|
|
236
|
+
| `globalMiddlewares` | `any[]` | ❌ | Middlewares applied to every request |
|
|
237
|
+
| `loadBalancer` | `TLoadBalancingStrategy` | ❌ | Default: `round-robin` |
|
|
238
|
+
| `enableLogging` | `boolean` | ❌ | Logs request duration when `true` |
|
|
239
|
+
| `enableMetrics` | `boolean` | ❌ | Reserved for future metrics collection |
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### `IRouteConfig` interface
|
|
244
|
+
|
|
245
|
+
| Property | Type | Required | Description |
|
|
246
|
+
|------------------|-------------------------|----------|---------------------------------------------------------|
|
|
247
|
+
| `path` | `string` | ✅ | URL path prefix (e.g. `/api/users`) |
|
|
248
|
+
| `method` | `HttpMethod` | ✅ | `get`, `post`, `put`, `delete`, `patch`, `options`… |
|
|
249
|
+
| `target` | `string \| string[]` | ✅ | Target URL(s). Array enables load balancing. |
|
|
250
|
+
| `middlewares` | `any[]` | ❌ | Route-specific middlewares (run after global ones) |
|
|
251
|
+
| `timeout` | `number` | ❌ | Request timeout in ms (default: `30000`) |
|
|
252
|
+
| `retries` | `number` | ❌ | Number of retry attempts on failure |
|
|
253
|
+
| `circuitBreaker` | `ICircuitBreakerConfig` | ❌ | Circuit breaker configuration |
|
|
254
|
+
| `serviceName` | `string` | ❌ | Explicit service name override for registry lookup |
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
### `IServiceConfig` interface
|
|
259
|
+
|
|
260
|
+
| Property | Type | Required | Description |
|
|
261
|
+
|---------------|----------|----------|------------------------------------------------|
|
|
262
|
+
| `name` | `string` | ✅ | Unique service identifier |
|
|
263
|
+
| `url` | `string` | ✅ | Base URL of the service |
|
|
264
|
+
| `healthCheck` | `string` | ❌ | Health check endpoint path (e.g. `/health`) |
|
|
265
|
+
| `weight` | `number` | ❌ | Weight for `weighted` load balancing strategy |
|
|
266
|
+
| `timeout` | `number` | ❌ | Default request timeout in ms |
|
|
267
|
+
| `retries` | `number` | ❌ | Default retry count |
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
### `ICircuitBreakerConfig` interface
|
|
272
|
+
|
|
273
|
+
| Property | Type | Description |
|
|
274
|
+
|-----------------------|----------|--------------------------------------------------------|
|
|
275
|
+
| `failureThreshold` | `number` | Failures before the circuit opens |
|
|
276
|
+
| `resetTimeout` | `number` | Time in ms before transitioning to `half-open` |
|
|
277
|
+
| `halfOpenMaxAttempts` | `number` | Max probe requests allowed in `half-open` state |
|
|
278
|
+
|
|
279
|
+
Circuit states:
|
|
280
|
+
- **`closed`** — Normal operation. Requests are forwarded.
|
|
281
|
+
- **`open`** — Service is unhealthy. Requests fail fast (503).
|
|
282
|
+
- **`half-open`** — Limited probing to test recovery.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Load Balancing Strategies
|
|
287
|
+
|
|
288
|
+
| Strategy | Description |
|
|
289
|
+
|---------------------|---------------------------------------------------------------------|
|
|
290
|
+
| `round-robin` | Distributes requests evenly across all healthy instances (default) |
|
|
291
|
+
| `weighted` | Routes more traffic to instances with a higher `weight` value |
|
|
292
|
+
| `random` | Selects a healthy instance at random |
|
|
293
|
+
| `least-connections` | Selects the instance with the fewest active connections |
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
const gateway = new APIGateway({
|
|
297
|
+
loadBalancer: 'least-connections',
|
|
298
|
+
// ...
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Multi-target route (load balancing across instances):
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
{
|
|
306
|
+
path: '/api',
|
|
307
|
+
method: 'get',
|
|
308
|
+
target: [
|
|
309
|
+
'http://service-1:4000',
|
|
310
|
+
'http://service-2:4000',
|
|
311
|
+
'http://service-3:4000',
|
|
312
|
+
],
|
|
313
|
+
serviceName: 'my-service',
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Middleware
|
|
320
|
+
|
|
321
|
+
All middlewares follow an Express-compatible signature:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
type TMiddlewareFunction = (req: any, res: any, next: () => void) => void | Promise<void>;
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Global Middlewares
|
|
328
|
+
|
|
329
|
+
Applied to every matched request before the proxy forward:
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
const gateway = new APIGateway({
|
|
333
|
+
globalMiddlewares: [
|
|
334
|
+
new LoggingMiddleware('info'),
|
|
335
|
+
new RateLimitMiddleware(100, 60000),
|
|
336
|
+
new AuthMiddleware(['key-abc', 'key-xyz']),
|
|
337
|
+
],
|
|
338
|
+
// ...
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Add global middlewares at runtime:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
gateway.addMiddleware((req, res, next) => {
|
|
346
|
+
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
|
|
347
|
+
next();
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Route-Level Middlewares
|
|
352
|
+
|
|
353
|
+
Applied only to a specific route, after global middlewares:
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
{
|
|
357
|
+
path: '/admin',
|
|
358
|
+
method: 'get',
|
|
359
|
+
target: 'http://admin-service:5000',
|
|
360
|
+
middlewares: [
|
|
361
|
+
new AuthMiddleware(['admin-key']),
|
|
362
|
+
(req, res, next) => {
|
|
363
|
+
if (!req.headers['x-admin-token']) {
|
|
364
|
+
res.statusCode = 403;
|
|
365
|
+
res.end(JSON.stringify({ error: 'Forbidden' }));
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
next();
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Built-in Middlewares
|
|
375
|
+
|
|
376
|
+
#### `AuthMiddleware`
|
|
377
|
+
|
|
378
|
+
Validates `x-api-key` header (or `?apiKey=` query param):
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import { AuthMiddleware } from 'opticore-api-gateway';
|
|
382
|
+
|
|
383
|
+
new AuthMiddleware(['secret-key-1', 'secret-key-2'])
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
- Returns `401` if no key is provided
|
|
387
|
+
- Returns `403` if the key is invalid
|
|
388
|
+
|
|
389
|
+
#### `RateLimitMiddleware`
|
|
390
|
+
|
|
391
|
+
Sliding-window rate limiting per client IP:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
import { RateLimitMiddleware } from 'opticore-api-gateway';
|
|
395
|
+
|
|
396
|
+
new RateLimitMiddleware(100, 60000) // 100 requests per 60 seconds
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Sets `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` response headers.
|
|
400
|
+
Returns `429` when the limit is exceeded.
|
|
401
|
+
|
|
402
|
+
#### `LoggingMiddleware`
|
|
403
|
+
|
|
404
|
+
Logs method, URL, status code, and duration on response finish:
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
import { LoggingMiddleware } from 'opticore-api-gateway';
|
|
408
|
+
|
|
409
|
+
new LoggingMiddleware('info') // 'info' | 'debug' | 'error'
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
#### `ValidationMiddleware`
|
|
413
|
+
|
|
414
|
+
Validates request body (or query/params) using `opticore-validator`:
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
import { ValidationMiddleware } from 'opticore-api-gateway';
|
|
418
|
+
|
|
419
|
+
new ValidationMiddleware(mySchema, 'body') // 'body' | 'query' | 'params' | 'all'
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Returns `400` with field-level errors if validation fails.
|
|
423
|
+
|
|
424
|
+
### Creating Custom Middlewares
|
|
425
|
+
|
|
426
|
+
Extend `BaseMiddleware` and implement `handle()`:
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
import { BaseMiddleware, TMiddlewareFunction } from 'opticore-api-gateway';
|
|
430
|
+
|
|
431
|
+
export class CorsMiddleware extends BaseMiddleware {
|
|
432
|
+
handle(): TMiddlewareFunction {
|
|
433
|
+
return (req, res, next) => {
|
|
434
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
435
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
436
|
+
next();
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## API Reference
|
|
445
|
+
|
|
446
|
+
### `APIGateway`
|
|
447
|
+
|
|
448
|
+
Main gateway class.
|
|
449
|
+
|
|
450
|
+
**Constructor**
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
new APIGateway(config: IGatewayConfig)
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**Methods**
|
|
457
|
+
|
|
458
|
+
| Method | Description |
|
|
459
|
+
|----------------------------------------------|--------------------------------------------------------------------------|
|
|
460
|
+
| `start(): Promise<void>` | Starts a standalone HTTP server on the configured port |
|
|
461
|
+
| `stop(): Promise<void>` | Gracefully shuts down the HTTP server |
|
|
462
|
+
| `addRoute(config: IRouteConfig)` | Dynamically registers a new route at runtime |
|
|
463
|
+
| `addMiddleware(fn: TMiddlewareFunction)` | Adds a global middleware at runtime |
|
|
464
|
+
| `registerService(service: IServiceConfig)` | Registers a new service in the service registry |
|
|
465
|
+
| `getOpticoreRoutes(): TFeatureRoutes[]` | Returns routes compatible with `WebServerCore.onStartServer()` |
|
|
466
|
+
| `getExpressMiddleware()` | Returns global middlewares as a single Express middleware function |
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
### `GatewayRoute`
|
|
471
|
+
|
|
472
|
+
Encapsulates a single route and its Opticore integration.
|
|
473
|
+
|
|
474
|
+
**Constructor**
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
new GatewayRoute(config: IRouteConfig, handler: TRouteHandler, localLanguage: string)
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**Methods**
|
|
481
|
+
|
|
482
|
+
| Method | Return Type | Description |
|
|
483
|
+
|---------------------------------------------------------------|---------------------------|----------------------------------------------|
|
|
484
|
+
| `getConfig()` | `IRouteConfig` | Returns the route configuration |
|
|
485
|
+
| `getStandaloneRoute(strategy?, options?)` | Express Router | Returns the standalone Express router |
|
|
486
|
+
| `createMultipleRouterConfig(controller)` | `IMultipleRouterConfig` | Generates an Opticore multi-router config |
|
|
487
|
+
| `createCollectionRouter(controller, routes)` | `void` | Initializes a collection router |
|
|
488
|
+
| `getMultipleRouteDefinition()` | `IMultipleRouteDefinition`| Returns the collection router definition |
|
|
489
|
+
| `GatewayRoute.createOpticoreRouteDefinition(path, handler)` | `{ path, handler }` | Static factory for Opticore route entries |
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
### `ServiceRegistry`
|
|
494
|
+
|
|
495
|
+
Manages service instances and health state.
|
|
496
|
+
|
|
497
|
+
| Method | Description |
|
|
498
|
+
|--------------------------------------------|---------------------------------------------------------|
|
|
499
|
+
| `registerService(service: IServiceConfig)` | Registers a new service and starts health check |
|
|
500
|
+
| `getHealthyInstances(serviceName: string)` | Returns healthy (circuit-closed) instances |
|
|
501
|
+
| `recordSuccess(serviceName, url)` | Resets failure counters; may close circuit |
|
|
502
|
+
| `recordFailure(serviceName, url)` | Increments failure count; may open circuit |
|
|
503
|
+
| `deregisterService(serviceName, url)` | Removes an instance and clears its health check |
|
|
504
|
+
| `getAllServices()` | Returns the full service map |
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
### `LoadBalancer`
|
|
509
|
+
|
|
510
|
+
Selects a service instance based on the configured strategy.
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
const lb = new LoadBalancer('weighted');
|
|
514
|
+
const instance = lb.selectInstance('user-service', healthyInstances);
|
|
515
|
+
lb.setStrategy('round-robin');
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
### `HttpClient`
|
|
521
|
+
|
|
522
|
+
Low-level HTTP/HTTPS client used internally to forward requests.
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
const client = new HttpClient();
|
|
526
|
+
const response = await client.request('http://service:4000/api', {
|
|
527
|
+
method: 'POST',
|
|
528
|
+
headers: { 'Content-Type': 'application/json' },
|
|
529
|
+
body: JSON.stringify({ key: 'value' }),
|
|
530
|
+
timeout: 5000,
|
|
531
|
+
});
|
|
532
|
+
// response: { status, headers, body }
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
Convenience methods: `client.get(url)`, `client.post(url, body)`, `client.put(url, body)`, `client.patch(url, body)`, `client.delete(url)`.
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
## Types & Interfaces
|
|
540
|
+
|
|
541
|
+
| Name | Description |
|
|
542
|
+
|--------------------------|--------------------------------------------------------------|
|
|
543
|
+
| `TMiddlewareFunction` | Express-compatible middleware signature |
|
|
544
|
+
| `TGatewayContext` | Opticore context extended with `serviceUrl`, `serviceInstance`, `gatewayStartTime` |
|
|
545
|
+
| `TLoadBalancingStrategy` | `'round-robin' \| 'weighted' \| 'random' \| 'least-connections'` |
|
|
546
|
+
| `IGatewayConfig` | Root gateway configuration |
|
|
547
|
+
| `IRouteConfig` | Individual route configuration |
|
|
548
|
+
| `IServiceConfig` | Backend service declaration |
|
|
549
|
+
| `IServiceInstance` | Runtime service instance with health state |
|
|
550
|
+
| `ICircuitBreakerConfig` | Circuit breaker thresholds |
|
|
551
|
+
| `IHttpRequestOptions` | Options passed to `HttpClient.request()` |
|
|
552
|
+
| `IHttpResponse` | Response shape returned by `HttpClient` |
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## Advanced Usage
|
|
557
|
+
|
|
558
|
+
### Adding a route at runtime
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
gateway.addRoute({
|
|
562
|
+
path: '/notifications',
|
|
563
|
+
method: 'get',
|
|
564
|
+
target: 'http://notification-service:6000',
|
|
565
|
+
serviceName: 'notification-service',
|
|
566
|
+
timeout: 3000,
|
|
567
|
+
retries: 2,
|
|
568
|
+
});
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### Custom load balancer strategy
|
|
572
|
+
|
|
573
|
+
Implement `ILoadBalancerStrategy` to plug in your own logic:
|
|
574
|
+
|
|
575
|
+
```typescript
|
|
576
|
+
import { ILoadBalancerStrategy } from 'opticore-api-gateway';
|
|
577
|
+
import { IServiceInstance } from 'opticore-api-gateway';
|
|
578
|
+
|
|
579
|
+
export class StickySessionStrategy implements ILoadBalancerStrategy {
|
|
580
|
+
selectInstance(serviceName: string, instances: IServiceInstance[]): IServiceInstance | null {
|
|
581
|
+
// Custom sticky-session logic based on session ID, user ID, etc.
|
|
582
|
+
return instances[0] ?? null;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Accessing the gateway context
|
|
588
|
+
|
|
589
|
+
`TGatewayContext` extends Opticore's `ICustomContext` with gateway-specific fields:
|
|
590
|
+
|
|
591
|
+
```typescript
|
|
592
|
+
type TGatewayContext = ICustomContext & {
|
|
593
|
+
serviceUrl?: string; // Resolved target URL
|
|
594
|
+
serviceInstance?: IServiceInstance; // Selected backend instance
|
|
595
|
+
gatewayStartTime?: number; // Request start timestamp (ms)
|
|
596
|
+
};
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
---
|
|
600
|
+
|
|
601
|
+
## Error Handling
|
|
602
|
+
|
|
603
|
+
| Scenario | HTTP Status | Response Body |
|
|
604
|
+
|----------------------------|-------------|--------------------------------------------------|
|
|
605
|
+
| No healthy instances | `503` | `{ "error": "Service unavailable" }` |
|
|
606
|
+
| Load balancer returns null | `503` | `{ "error": "No healthy instances available" }` |
|
|
607
|
+
| Forwarding / proxy error | `502` | `{ "error": "Bad Gateway", "message": "..." }` |
|
|
608
|
+
| Middleware error | `500` | `{ "error": "Internal Server Error" }` |
|
|
609
|
+
| Route not found | `404` | `{ "error": "Route not found" }` |
|
|
610
|
+
| Missing API key | `401` | `{ "error": "API key required" }` |
|
|
611
|
+
| Invalid API key | `403` | `{ "error": "Invalid API key" }` |
|
|
612
|
+
| Rate limit exceeded | `429` | `{ "error": "Too many requests", "retryAfter": N }` |
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
## License
|
|
617
|
+
|
|
618
|
+
MIT
|