rpc-express-toolkit 3.0.1 → 4.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 +27 -1028
- package/package.json +1 -1
- package/src/clients/rpc-client.js +392 -350
- package/src/clients/rpc-client.min.js +1 -1
- package/src/clients/rpc-client.min.mjs +1 -1
- package/src/clients/rpc-client.mjs +82 -37
- package/src/index.d.ts +10 -14
- package/src/index.js +44 -31
package/README.md
CHANGED
|
@@ -2,1071 +2,70 @@
|
|
|
2
2
|
|
|
3
3
|
[](LICENSE)
|
|
4
4
|
[](https://www.npmjs.com/package/rpc-express-toolkit)
|
|
5
|
-
<!-- [](https://github.com/n-car/rpc-express-toolkit/actions/workflows/ci.yml) -->
|
|
6
|
-
<!-- [](https://coveralls.io/github/n-car/rpc-express-toolkit?branch=main) -->
|
|
7
5
|
|
|
8
|
-
|
|
6
|
+
Enterprise-ready JSON-RPC 2.0 toolkit for Express.js with simplified APIs, middleware, schema validation, batch support, and optional safe type disambiguation.
|
|
9
7
|
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
- [Features](#features)
|
|
13
|
-
- [Installation](#installation)
|
|
14
|
-
- [Quick Start](#quick-start)
|
|
15
|
-
- [Advanced Usage](#advanced-usage)
|
|
16
|
-
- [Configuration Options](#configuration-options)
|
|
17
|
-
- [Schema Validation](#schema-validation)
|
|
18
|
-
- [Middleware System](#middleware-system)
|
|
19
|
-
- [Batch Requests](#batch-requests)
|
|
20
|
-
- [Structured Logging](#structured-logging)
|
|
21
|
-
- [Built-in Middlewares](#built-in-middlewares)
|
|
22
|
-
- [API Reference](#api-reference)
|
|
23
|
-
- [Examples](#examples)
|
|
24
|
-
- [Contributing](#contributing)
|
|
25
|
-
- [License](#license)
|
|
26
|
-
|
|
27
|
-
## Features
|
|
28
|
-
|
|
29
|
-
### Core Features
|
|
30
|
-
- **JSON-RPC 2.0 Compliance:** Fully adheres to the JSON-RPC 2.0 specification
|
|
31
|
-
- **Server & Client Support:** Provides both server endpoint and client classes
|
|
32
|
-
- **Asynchronous Support:** Handle asynchronous operations seamlessly with Promises
|
|
33
|
-
- **BigInt & Date Serialization:** Robust serialization/deserialization with timezone support
|
|
34
|
-
- **Cross-Platform:** Works in both browser and Node.js environments
|
|
35
|
-
- **Error Handling:** Comprehensive error responses with sanitization options
|
|
36
|
-
|
|
37
|
-
### Enterprise Features
|
|
38
|
-
- **🔧 Structured Logging:** Configurable logging with multiple transports and levels
|
|
39
|
-
- **⚡ Middleware System:** Extensible middleware with built-in rate limiting, CORS, auth
|
|
40
|
-
- **✅ Schema Validation:** JSON Schema validation with Ajv and schema builder utilities
|
|
41
|
-
- **📦 Batch Processing:** Efficient batch request handling with concurrent processing
|
|
42
|
-
- **📊 Health & Metrics:** Built-in health checks and metrics endpoints
|
|
43
|
-
- **🔒 Security:** Method whitelisting, authentication, and error sanitization
|
|
44
|
-
- **🎯 Performance:** Request timing, caching support, and optimized serialization
|
|
8
|
+
## Quick Start
|
|
45
9
|
|
|
46
|
-
|
|
10
|
+
### Installation
|
|
47
11
|
|
|
48
12
|
```bash
|
|
49
13
|
npm install rpc-express-toolkit
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Or using yarn:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
14
|
+
# or
|
|
55
15
|
yarn add rpc-express-toolkit
|
|
56
16
|
```
|
|
57
17
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
## Quick Start
|
|
61
|
-
|
|
62
|
-
### Basic Setup
|
|
18
|
+
### Server
|
|
63
19
|
|
|
64
20
|
```javascript
|
|
65
21
|
const express = require('express');
|
|
66
22
|
const { RpcEndpoint } = require('rpc-express-toolkit');
|
|
67
23
|
|
|
68
24
|
const app = express();
|
|
69
|
-
//
|
|
70
|
-
app.use(express.json());
|
|
25
|
+
app.use(express.json()); // required
|
|
71
26
|
|
|
72
|
-
|
|
73
|
-
const context = { database: db, config: config };
|
|
27
|
+
const context = { database: db, config };
|
|
74
28
|
|
|
75
|
-
// Create JSON-RPC server (validates JSON middleware compatibility)
|
|
76
29
|
const rpc = new RpcEndpoint(app, context);
|
|
77
|
-
|
|
78
|
-
// Add simple methods
|
|
79
|
-
rpc.addMethod('add', (req, ctx, params) => {
|
|
80
|
-
const { a, b } = params;
|
|
81
|
-
return a + b;
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
rpc.addMethod('getUser', async (req, ctx, params) => {
|
|
85
|
-
const { userId } = params;
|
|
86
|
-
const user = await ctx.database.users.findById(userId);
|
|
87
|
-
return user;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
app.listen(3000, () => {
|
|
91
|
-
console.log('Server running on port 3000');
|
|
92
|
-
});
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
### Client Usage
|
|
97
|
-
|
|
98
|
-
```javascript
|
|
99
|
-
const { RpcClient } = require('rpc-express-toolkit');
|
|
100
|
-
|
|
101
|
-
// Node.js client
|
|
102
|
-
const client = new RpcClient('http://localhost:3000/api');
|
|
103
|
-
|
|
104
|
-
// Make RPC calls
|
|
105
|
-
const result = await client.call('add', { a: 5, b: 3 });
|
|
106
|
-
console.log(result); // 8
|
|
107
|
-
|
|
108
|
-
// Browser client (include the client script)
|
|
109
|
-
const client = new RpcClient('/api');
|
|
110
|
-
const user = await client.call('getUser', { userId: 123 });
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
#### ⚠️ SSL and self-signed certificates in development (Node.js)
|
|
115
|
-
|
|
116
|
-
If you need to connect to a server with a self-signed certificate during development, set the following environment variable **before** starting your Node.js process:
|
|
117
|
-
|
|
118
|
-
```js
|
|
119
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
**Warning:**
|
|
123
|
-
- This disables SSL certificate validation for all HTTPS requests in the Node.js process.
|
|
124
|
-
- Use this only in development environments!
|
|
125
|
-
- Never commit or enable this setting in production.
|
|
126
|
-
|
|
127
|
-
Advanced options like `agent` and `ca` have been removed for simplicity and compatibility with modern Node.js. This is the only universal and reliable way to bypass SSL validation for development with self-signed certificates.
|
|
128
|
-
|
|
129
|
-
## Advanced Usage
|
|
130
|
-
|
|
131
|
-
### Configuration Options
|
|
132
|
-
|
|
133
|
-
```javascript
|
|
134
|
-
const rpc = new RpcEndpoint(app, context, {
|
|
135
|
-
endpoint: '/api',
|
|
136
|
-
|
|
137
|
-
// Logging configuration
|
|
138
|
-
logging: {
|
|
139
|
-
level: 'info',
|
|
140
|
-
format: 'json',
|
|
141
|
-
transports: ['console', 'file'],
|
|
142
|
-
file: {
|
|
143
|
-
filename: 'rpc.log',
|
|
144
|
-
maxsize: 10485760, // 10MB
|
|
145
|
-
maxFiles: 5
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
|
|
149
|
-
// Built-in middleware
|
|
150
|
-
cors: {
|
|
151
|
-
origin: ['http://localhost:3000'],
|
|
152
|
-
credentials: true
|
|
153
|
-
},
|
|
154
|
-
|
|
155
|
-
rateLimit: {
|
|
156
|
-
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
157
|
-
max: 100 // limit each IP to 100 requests per windowMs
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
auth: {
|
|
161
|
-
required: true,
|
|
162
|
-
verify: (req) => {
|
|
163
|
-
const token = req.headers.authorization;
|
|
164
|
-
return verifyToken(token);
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
methodWhitelist: ['add', 'subtract', 'getUser'],
|
|
169
|
-
|
|
170
|
-
// Schema validation
|
|
171
|
-
validation: {
|
|
172
|
-
strict: true,
|
|
173
|
-
coerceTypes: true,
|
|
174
|
-
removeAdditional: true
|
|
175
|
-
},
|
|
176
|
-
|
|
177
|
-
// Safe serialization options
|
|
178
|
-
safeStringEnabled: true, // Add 'S:' prefix to strings (default: true)
|
|
179
|
-
safeDateEnabled: true, // Add 'D:' prefix to dates (default: true)
|
|
180
|
-
warnOnUnsafeString: true, // Warn when BigInt found with safeStringEnabled=false (default: true)
|
|
181
|
-
warnOnUnsafeDate: true, // Warn when Date found with safeDateEnabled=false (default: true)
|
|
182
|
-
|
|
183
|
-
// Other middleware options
|
|
184
|
-
cors: true,
|
|
185
|
-
rateLimit: { windowMs: 15 * 60 * 1000, max: 100 },
|
|
186
|
-
jsonOptions: { // Options to pass to express.json()
|
|
187
|
-
limit: '10mb', // Body size limit (default: uses maxBodySize)
|
|
188
|
-
strict: true, // Only parse arrays and objects
|
|
189
|
-
type: 'application/json' // Content-Type to parse
|
|
190
|
-
},
|
|
191
|
-
warnOnUnsafeDate: true, // Warn when Date found with safeDateEnabled=false (default: true)
|
|
192
|
-
|
|
193
|
-
// Feature flags
|
|
194
|
-
healthCheck: true,
|
|
195
|
-
metrics: true,
|
|
196
|
-
timing: true
|
|
197
|
-
});
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Schema Validation
|
|
201
|
-
|
|
202
|
-
```javascript
|
|
203
|
-
const { SchemaBuilder, commonSchemas } = require('rpc-express-toolkit');
|
|
204
|
-
|
|
205
|
-
// Using schema builder
|
|
206
|
-
const addSchema = SchemaBuilder.object({
|
|
207
|
-
a: SchemaBuilder.number().required(),
|
|
208
|
-
b: SchemaBuilder.number().required()
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// Register method with schema
|
|
212
|
-
rpc.addMethod('add', {
|
|
213
|
-
handler: (req, ctx, params) => params.a + params.b,
|
|
214
|
-
schema: addSchema
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
// Using common schemas
|
|
218
|
-
rpc.addMethod('getUser', {
|
|
219
|
-
handler: async (req, ctx, params) => {
|
|
220
|
-
return await ctx.database.users.findById(params.userId);
|
|
221
|
-
},
|
|
222
|
-
schema: {
|
|
223
|
-
type: 'object',
|
|
224
|
-
properties: {
|
|
225
|
-
userId: commonSchemas.positiveInteger
|
|
226
|
-
},
|
|
227
|
-
required: ['userId']
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### Middleware System
|
|
233
|
-
|
|
234
|
-
**JSON Middleware Validation**
|
|
235
|
-
|
|
236
|
-
RpcEndpoint validates that your Express router can handle JSON parsing properly:
|
|
237
|
-
|
|
238
|
-
```javascript
|
|
239
|
-
const express = require('express');
|
|
240
|
-
const { RpcEndpoint } = require('rpc-express-toolkit');
|
|
241
|
-
|
|
242
|
-
const app = express();
|
|
243
|
-
app.use(express.json()); // You must configure JSON middleware
|
|
244
|
-
|
|
245
|
-
try {
|
|
246
|
-
// RpcEndpoint validates JSON middleware compatibility during construction
|
|
247
|
-
const rpc = new RpcEndpoint(app, context);
|
|
248
|
-
console.log('✅ JSON middleware validation passed');
|
|
249
|
-
} catch (error) {
|
|
250
|
-
console.error('❌ JSON middleware validation failed:', error.message);
|
|
251
|
-
// This router cannot handle JSON-RPC requests properly.
|
|
252
|
-
}
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
**Benefits of JSON Middleware Validation:**
|
|
256
|
-
|
|
257
|
-
- 🛡️ **Early Error Detection**: Catches JSON parsing issues at startup, not runtime
|
|
258
|
-
- 🎯 **Express Version Compatibility**: Ensures Express ≥4.16.0 for `express.json()` support
|
|
259
|
-
- 📋 **Clear Error Messages**: Provides specific solutions for configuration problems
|
|
260
|
-
- 🔍 **Debug Information**: Logs validation status for troubleshooting
|
|
261
|
-
|
|
262
|
-
**JSON Middleware Validation:**
|
|
263
|
-
|
|
264
|
-
The system validates JSON middleware functionality during RpcEndpoint construction:
|
|
265
|
-
|
|
266
|
-
**Customizing JSON Options:**
|
|
267
|
-
```javascript
|
|
268
|
-
const rpc = new RpcEndpoint(app, context, {
|
|
269
|
-
endpoint: '/api',
|
|
270
|
-
jsonOptions: {
|
|
271
|
-
limit: '50mb', // Increase body size limit
|
|
272
|
-
strict: false, // Allow non-objects/arrays
|
|
273
|
-
type: ['application/json', 'text/plain'] // Accept multiple content types
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
**Common JSON Validation Errors:**
|
|
279
|
-
|
|
280
|
-
```javascript
|
|
281
|
-
// Error: Express version too old
|
|
282
|
-
// Solution: Update Express to ≥4.16.0
|
|
283
|
-
npm install express@latest
|
|
284
|
-
|
|
285
|
-
// Error: No JSON middleware configured
|
|
286
|
-
// Solution: Add JSON middleware to your router
|
|
287
|
-
app.use(express.json());
|
|
288
|
-
|
|
289
|
-
// Error: body-parser required for older Express
|
|
290
|
-
// Solution: Install and configure body-parser
|
|
291
|
-
npm install body-parser
|
|
292
|
-
app.use(require('body-parser').json());
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
## API Reference
|
|
296
|
-
|
|
297
|
-
### RpcEndpoint Class
|
|
298
|
-
|
|
299
|
-
#### Constructor
|
|
300
|
-
|
|
301
|
-
```javascript
|
|
302
|
-
new RpcEndpoint(router, context, options)
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
**Parameters:**
|
|
306
|
-
- `router` (Express.Router): Express router instance
|
|
307
|
-
- `context` (Object): Context object passed to method handlers
|
|
308
|
-
- `options` (Object|string): Configuration options or endpoint path
|
|
309
|
-
|
|
310
|
-
**Options:**
|
|
311
|
-
- `endpoint` (string): Endpoint path (default: '/api')
|
|
312
|
-
- `logging` (Object): Logging configuration
|
|
313
|
-
- `cors` (Object): CORS configuration
|
|
314
|
-
- `rateLimit` (Object): Rate limiting configuration
|
|
315
|
-
- `auth` (Object): Authentication configuration
|
|
316
|
-
- `methodWhitelist` (Array): Allowed method names
|
|
317
|
-
- `validation` (Object): Schema validation options
|
|
318
|
-
- `healthCheck` (boolean): Enable health check endpoint
|
|
319
|
-
- `metrics` (boolean): Enable metrics endpoint
|
|
320
|
-
- `timing` (boolean): Enable request timing
|
|
321
|
-
|
|
322
|
-
#### Methods
|
|
323
|
-
|
|
324
|
-
##### `addMethod(name, handlerOrConfig, schema)`
|
|
325
|
-
|
|
326
|
-
Register a new JSON-RPC method.
|
|
327
|
-
|
|
328
|
-
```javascript
|
|
329
|
-
// Simple method
|
|
330
|
-
rpc.addMethod('add', (req, ctx, params) => {
|
|
331
|
-
return params.a + params.b;
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
// Method with schema validation
|
|
335
|
-
rpc.addMethod('getUser', {
|
|
336
|
-
handler: async (req, ctx, params) => {
|
|
337
|
-
return await ctx.db.users.findById(params.userId);
|
|
338
|
-
},
|
|
339
|
-
schema: {
|
|
340
|
-
type: 'object',
|
|
341
|
-
properties: {
|
|
342
|
-
userId: { type: 'number', minimum: 1 }
|
|
343
|
-
},
|
|
344
|
-
required: ['userId']
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
##### `use(hook, middleware)`
|
|
350
|
-
|
|
351
|
-
Add middleware for specific execution hooks.
|
|
352
|
-
|
|
353
|
-
```javascript
|
|
354
|
-
rpc.use('beforeCall', async (context) => {
|
|
355
|
-
// Pre-processing
|
|
356
|
-
return context;
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
rpc.use('afterCall', async (context) => {
|
|
360
|
-
// Post-processing
|
|
361
|
-
return context;
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
rpc.use('onError', async (context) => {
|
|
365
|
-
// Error handling
|
|
366
|
-
});
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
##### `removeMethod(name)`
|
|
370
|
-
|
|
371
|
-
Remove a registered method.
|
|
372
|
-
|
|
373
|
-
```javascript
|
|
374
|
-
rpc.removeMethod('methodName');
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
##### `getMethod(name)`
|
|
378
|
-
|
|
379
|
-
Get method configuration.
|
|
380
|
-
|
|
381
|
-
```javascript
|
|
382
|
-
const config = rpc.getMethod('methodName');
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
##### `listMethods()`
|
|
386
|
-
|
|
387
|
-
List all registered methods.
|
|
388
|
-
|
|
389
|
-
```javascript
|
|
390
|
-
const methods = rpc.listMethods();
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
##### `getMetrics()`
|
|
394
|
-
|
|
395
|
-
Get current metrics data.
|
|
396
|
-
|
|
397
|
-
```javascript
|
|
398
|
-
const metrics = rpc.getMetrics();
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
#### Properties
|
|
402
|
-
|
|
403
|
-
- `endpoint`: The endpoint path
|
|
404
|
-
- `methods`: All registered methods
|
|
405
|
-
- `logger`: Logger instance
|
|
406
|
-
- `middleware`: Middleware manager
|
|
407
|
-
- `validator`: Schema validator
|
|
408
|
-
|
|
409
|
-
### RpcClient Class
|
|
410
|
-
|
|
411
|
-
#### Constructor
|
|
412
|
-
|
|
413
|
-
```javascript
|
|
414
|
-
new RpcClient(baseUrl, options)
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
#### Methods
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
##### `call(method, params, options)`
|
|
421
|
-
|
|
422
|
-
Make a single RPC call.
|
|
423
|
-
|
|
424
|
-
```javascript
|
|
425
|
-
// If the method does not require parameters, you can omit the params argument:
|
|
426
|
-
const result = await client.call('ping');
|
|
427
|
-
|
|
428
|
-
// If you pass params as undefined or null, the field will be omitted from the JSON-RPC payload (per spec):
|
|
429
|
-
await client.call('ping', undefined);
|
|
430
|
-
await client.call('ping', null);
|
|
431
|
-
|
|
432
|
-
// If you pass an object or array, it will be sent as params:
|
|
433
|
-
await client.call('add', { a: 1, b: 2 });
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
> **Note:**
|
|
437
|
-
> The `params` field is omitted from the JSON-RPC request if you pass `undefined` or `null`, as required by the JSON-RPC 2.0 specification. If you pass an object or array, it will be included as `params`.
|
|
438
|
-
|
|
439
|
-
##### `batch(requests)`
|
|
440
|
-
|
|
441
|
-
Make batch RPC calls.
|
|
442
|
-
|
|
443
|
-
```javascript
|
|
444
|
-
const results = await client.batch([
|
|
445
|
-
{ method: 'add', params: { a: 1, b: 2 } },
|
|
446
|
-
{ method: 'subtract', params: { a: 5, b: 3 } }
|
|
447
|
-
]);
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
##### `notify(method, params)`
|
|
451
|
-
|
|
452
|
-
Send notification (no response expected).
|
|
453
|
-
|
|
454
|
-
```javascript
|
|
455
|
-
await client.notify('logEvent', { event: 'user_login' });
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
### Logger Class
|
|
459
|
-
|
|
460
|
-
Structured logging with configurable transports and levels.
|
|
461
|
-
|
|
462
|
-
```javascript
|
|
463
|
-
const logger = new Logger({
|
|
464
|
-
level: 'info',
|
|
465
|
-
format: 'json',
|
|
466
|
-
transports: ['console', 'file']
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
logger.info('Message', { key: 'value' });
|
|
470
|
-
logger.error('Error', { error: err.message });
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
### SchemaValidator Class
|
|
474
|
-
|
|
475
|
-
JSON Schema validation with Ajv.
|
|
476
|
-
|
|
477
|
-
```javascript
|
|
478
|
-
const validator = new SchemaValidator({
|
|
479
|
-
strict: true,
|
|
480
|
-
coerceTypes: true
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
const result = validator.validate(data, schema);
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
### SchemaBuilder
|
|
487
|
-
|
|
488
|
-
Utility for building JSON schemas.
|
|
489
|
-
|
|
490
|
-
```javascript
|
|
491
|
-
const schema = SchemaBuilder.object({
|
|
492
|
-
name: SchemaBuilder.string().required(),
|
|
493
|
-
age: SchemaBuilder.number().min(0).max(150),
|
|
494
|
-
email: SchemaBuilder.string().format('email')
|
|
495
|
-
});
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
### BatchHandler
|
|
499
|
-
|
|
500
|
-
Handles batch request processing with concurrency control.
|
|
501
|
-
|
|
502
|
-
```javascript
|
|
503
|
-
const batchHandler = new BatchHandler(endpoint, {
|
|
504
|
-
maxConcurrency: 10,
|
|
505
|
-
timeout: 30000
|
|
506
|
-
});
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
## Examples
|
|
510
|
-
|
|
511
|
-
### Enterprise Configuration
|
|
512
|
-
|
|
513
|
-
```javascript
|
|
514
|
-
const express = require('express');
|
|
515
|
-
const { RpcEndpoint, SchemaBuilder } = require('rpc-express-toolkit');
|
|
516
|
-
|
|
517
|
-
const app = express();
|
|
518
|
-
app.use(express.json());
|
|
519
|
-
|
|
520
|
-
const rpc = new RpcEndpoint(app, { db, config }, {
|
|
521
|
-
endpoint: '/api/v1',
|
|
522
|
-
logging: {
|
|
523
|
-
level: 'info',
|
|
524
|
-
format: 'json',
|
|
525
|
-
transports: ['console', 'file'],
|
|
526
|
-
file: { filename: 'api.log' }
|
|
527
|
-
},
|
|
528
|
-
cors: {
|
|
529
|
-
origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'],
|
|
530
|
-
credentials: true
|
|
531
|
-
},
|
|
532
|
-
rateLimit: {
|
|
533
|
-
windowMs: 15 * 60 * 1000,
|
|
534
|
-
max: 100
|
|
535
|
-
},
|
|
536
|
-
auth: {
|
|
537
|
-
required: true,
|
|
538
|
-
verify: async (req) => {
|
|
539
|
-
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
540
|
-
return await verifyJWT(token);
|
|
541
|
-
}
|
|
542
|
-
},
|
|
543
|
-
validation: {
|
|
544
|
-
strict: true,
|
|
545
|
-
coerceTypes: true
|
|
546
|
-
},
|
|
547
|
-
healthCheck: true,
|
|
548
|
-
metrics: true
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
// Add methods with validation
|
|
552
|
-
rpc.addMethod('createUser', {
|
|
553
|
-
handler: async (req, ctx, params) => {
|
|
554
|
-
const user = await ctx.db.users.create(params);
|
|
555
|
-
return { id: user.id, message: 'User created successfully' };
|
|
556
|
-
},
|
|
557
|
-
schema: SchemaBuilder.object({
|
|
558
|
-
name: SchemaBuilder.string().min(1).max(100).required(),
|
|
559
|
-
email: SchemaBuilder.string().format('email').required(),
|
|
560
|
-
age: SchemaBuilder.number().min(0).max(150).optional()
|
|
561
|
-
})
|
|
562
|
-
});
|
|
30
|
+
rpc.addMethod('add', (req, ctx, params) => params.a + params.b);
|
|
563
31
|
|
|
564
32
|
app.listen(3000);
|
|
565
33
|
```
|
|
566
34
|
|
|
567
|
-
###
|
|
35
|
+
### Client
|
|
568
36
|
|
|
569
37
|
```javascript
|
|
570
|
-
// BigInt serialization
|
|
571
|
-
rpc.addMethod('getBigNumber', () => {
|
|
572
|
-
return BigInt('9007199254740991123456789'); // Automatically serialized to string
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
// Date serialization
|
|
576
|
-
rpc.addMethod('getCurrentTime', () => {
|
|
577
|
-
return new Date(); // Automatically serialized to ISO string
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
// Client receives properly typed data
|
|
581
|
-
const client = new RpcClient('http://localhost:3000/api');
|
|
582
|
-
const bigNum = await client.call('getBigNumber'); // Returns BigInt
|
|
583
|
-
const date = await client.call('getCurrentTime'); // Returns Date object
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
### Handling Numeric Strings with Leading Zeros
|
|
587
|
-
|
|
588
|
-
When working with numeric strings that need to preserve leading zeros (like IDs, codes, or references), be aware of BigInt auto-conversion behavior:
|
|
589
|
-
|
|
590
|
-
```javascript
|
|
591
|
-
// ✅ CORRECT: Numeric strings without 'n' suffix are preserved
|
|
592
|
-
await client.call('processCode', { code: '0123456' });
|
|
593
|
-
// → Server receives: '0123456' (string)
|
|
594
|
-
// → Client receives: '0123456' (string) ✅
|
|
595
|
-
|
|
596
|
-
// ❌ POTENTIAL ISSUE: Strings ending with 'n' are converted to BigInt
|
|
597
|
-
await client.call('processRef', { ref: '012312031203120301230123123n' });
|
|
598
|
-
// → Server receives: '012312031203120301230123123n' (string)
|
|
599
|
-
// → Client receives: 12312031203120301230123123n (BigInt, leading zero lost!) ❌
|
|
600
|
-
|
|
601
|
-
// ✅ SOLUTIONS for strings that should remain strings:
|
|
602
|
-
|
|
603
|
-
// Solution 1: Use schema validation to force string type
|
|
604
|
-
rpc.addMethod('processRef', (req, ctx, params) => {
|
|
605
|
-
// params.ref will always be a string due to schema validation
|
|
606
|
-
return { processed: params.ref };
|
|
607
|
-
}, {
|
|
608
|
-
type: 'object',
|
|
609
|
-
properties: {
|
|
610
|
-
ref: { type: 'string' } // Forces string type, prevents BigInt conversion
|
|
611
|
-
}
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
// Solution 2: Use escape prefix for problematic strings
|
|
615
|
-
await client.call('processRef', { ref: 'S:012312031203120301230123123n' });
|
|
616
|
-
// Server strips prefix: const actualRef = params.ref.replace(/^S:/, '');
|
|
617
|
-
|
|
618
|
-
// Solution 3: Use different suffix
|
|
619
|
-
await client.call('processRef', { ref: '012312031203120301230123123_id' });
|
|
620
|
-
// → Remains string: '012312031203120301230123123_id' ✅
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
**Note:** Only strings explicitly ending with 'n' and containing only digits are auto-converted to BigInt. This preserves leading zeros for most numeric strings while maintaining BigInt functionality for intentional use.
|
|
624
|
-
|
|
625
|
-
### Safe Serialization with Prefixes
|
|
626
|
-
|
|
627
|
-
The toolkit provides safe serialization options to prevent confusion between strings, BigInt values, and dates:
|
|
628
|
-
|
|
629
|
-
```javascript
|
|
630
|
-
// Enable safe serialization (default: true for both)
|
|
631
|
-
const rpc = new RpcEndpoint(app, context, {
|
|
632
|
-
safeStringEnabled: true, // Strings get 'S:' prefix
|
|
633
|
-
safeDateEnabled: true // Dates get 'D:' prefix
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
// Example serialization with safe options enabled:
|
|
637
|
-
rpc.addMethod('getComplexData', () => {
|
|
638
|
-
return {
|
|
639
|
-
message: "Hello World", // → "S:Hello World"
|
|
640
|
-
timestamp: new Date(), // → "D:2023-06-15T10:30:00.000Z"
|
|
641
|
-
count: 42n, // → "42n"
|
|
642
|
-
value: 123 // → 123 (unchanged)
|
|
643
|
-
};
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
// Client automatically deserializes based on server headers:
|
|
647
|
-
const client = new RpcClient('http://localhost:3000/api');
|
|
648
|
-
const result = await client.call('getComplexData');
|
|
649
|
-
// result.message: "Hello World" (string, prefix removed)
|
|
650
|
-
// result.timestamp: Date object
|
|
651
|
-
// result.count: 42n (BigInt)
|
|
652
|
-
// result.value: 123 (number)
|
|
653
|
-
```
|
|
654
|
-
|
|
655
|
-
#### Cross-Configuration Support
|
|
656
|
-
|
|
657
|
-
Client and server can have different safe options - they communicate automatically via HTTP headers:
|
|
658
|
-
|
|
659
|
-
```javascript
|
|
660
|
-
// Server with safe dates disabled
|
|
661
|
-
const rpc = new RpcEndpoint(app, context, {
|
|
662
|
-
safeStringEnabled: true, // S: prefix for strings
|
|
663
|
-
safeDateEnabled: false // No prefix for dates
|
|
664
|
-
});
|
|
665
|
-
|
|
666
|
-
// Client with safe dates enabled
|
|
667
|
-
const client = new RpcClient('http://localhost:3000/api', {
|
|
668
|
-
safeStringEnabled: true, // S: prefix for strings
|
|
669
|
-
safeDateEnabled: true // D: prefix for dates
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
// Data flows correctly despite different configurations:
|
|
673
|
-
// Client sends: { date: "D:2023-01-01T00:00:00.000Z" }
|
|
674
|
-
// Server receives: Date object (auto-deserialized using client's headers)
|
|
675
|
-
// Server responds: { date: "2023-01-01T00:00:00.000Z" } (no prefix, per server config)
|
|
676
|
-
// Client receives: Date object (auto-deserialized using server's headers)
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
#### Benefits of Safe Prefixes
|
|
680
|
-
|
|
681
|
-
- **Prevents ambiguity:** `"123n"` vs `"S:123n"` (string) vs `123n` (BigInt)
|
|
682
|
-
- **Cross-configuration:** Different client/server settings work seamlessly
|
|
683
|
-
- **Compact:** Short prefixes (`S:`, `D:`) minimize payload overhead
|
|
684
|
-
- **Automatic:** No manual intervention required, handled by headers
|
|
685
|
-
- **Backward compatible:** Can be disabled if needed
|
|
686
|
-
|
|
687
|
-
### Disabling Serialization Warnings
|
|
688
|
-
|
|
689
|
-
When safe options are disabled (`safeStringEnabled: false` or `safeDateEnabled: false`), the toolkit warns about potential data type confusion. You can disable these warnings to reduce console noise:
|
|
690
|
-
|
|
691
|
-
```javascript
|
|
692
|
-
// Disable all serialization warnings
|
|
693
|
-
const rpc = new RpcEndpoint(app, context, {
|
|
694
|
-
safeStringEnabled: false, // Disable safe strings
|
|
695
|
-
safeDateEnabled: false, // Disable safe dates
|
|
696
|
-
warnOnUnsafeString: false, // Disable BigInt warnings
|
|
697
|
-
warnOnUnsafeDate: false // Disable Date warnings
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
// Disable only specific warnings
|
|
701
|
-
const rpc = new RpcEndpoint(app, context, {
|
|
702
|
-
safeStringEnabled: false, // Still disabled
|
|
703
|
-
safeDateEnabled: false, // Still disabled
|
|
704
|
-
warnOnUnsafeString: false, // No BigInt warnings
|
|
705
|
-
warnOnUnsafeDate: true // Keep Date warnings
|
|
706
|
-
});
|
|
707
|
-
|
|
708
|
-
// Client also supports warning options
|
|
709
|
-
const client = new RpcClient('http://localhost:3000/api', {
|
|
710
|
-
safeStringEnabled: false,
|
|
711
|
-
warnOnUnsafeString: false // Disable client-side warnings
|
|
712
|
-
});
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
**Warning types:**
|
|
716
|
-
- `warnOnUnsafeString`: Warns when BigInt values are serialized without `safeStringEnabled`
|
|
717
|
-
- `warnOnUnsafeDate`: Warns when Date values are serialized without `safeDateEnabled`
|
|
718
|
-
|
|
719
|
-
**Use cases for disabling warnings:**
|
|
720
|
-
- Production environments where warnings would clutter logs
|
|
721
|
-
- Legacy applications that intentionally use unsafe serialization
|
|
722
|
-
- Bulk data processing where performance is critical
|
|
723
|
-
|
|
724
|
-
### Error Handling
|
|
725
|
-
|
|
726
|
-
```javascript
|
|
727
|
-
rpc.addMethod('mayFail', (req, ctx, params) => {
|
|
728
|
-
if (params.shouldFail) {
|
|
729
|
-
const error = new Error('Something went wrong');
|
|
730
|
-
error.code = -32001; // Custom error code
|
|
731
|
-
error.data = { details: 'Additional error info' };
|
|
732
|
-
throw error;
|
|
733
|
-
}
|
|
734
|
-
return 'success';
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
// Client error handling
|
|
738
|
-
try {
|
|
739
|
-
await client.call('mayFail', { shouldFail: true });
|
|
740
|
-
} catch (error) {
|
|
741
|
-
console.log(error.code); // -32001
|
|
742
|
-
console.log(error.message); // 'Something went wrong'
|
|
743
|
-
console.log(error.data); // { details: 'Additional error info' }
|
|
744
|
-
}
|
|
745
|
-
```
|
|
746
|
-
userId: 123,
|
|
747
|
-
action: 'login',
|
|
748
|
-
ip: req.ip
|
|
749
|
-
});
|
|
750
|
-
|
|
751
|
-
logger.error('Database error', {
|
|
752
|
-
error: err.message,
|
|
753
|
-
query: 'SELECT * FROM users',
|
|
754
|
-
duration: 150
|
|
755
|
-
});
|
|
756
|
-
|
|
757
|
-
// RPC calls are automatically logged
|
|
758
|
-
// [2024-01-01T10:00:00.000Z] INFO: RPC call - {"method":"getUser","params":{"userId":123},"id":"req-123","ip":"192.168.1.1"}
|
|
759
|
-
// [2024-01-01T10:00:00.050Z] INFO: RPC success - {"method":"getUser","id":"req-123","duration":50,"resultSize":245}
|
|
760
|
-
```
|
|
761
|
-
|
|
762
|
-
### Client Configuration
|
|
763
|
-
|
|
764
|
-
The `RpcClient` supports various configuration options for different environments, **inclusa la gestione di certificati self-signed**:
|
|
765
|
-
|
|
766
|
-
```javascript
|
|
767
|
-
const fs = require('fs');
|
|
768
38
|
const { RpcClient } = require('rpc-express-toolkit');
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
const client = new RpcClient('https://api.example.com/rpc');
|
|
772
|
-
|
|
773
|
-
// Client con header personalizzati
|
|
774
|
-
const clientWithHeaders = new RpcClient('https://api.example.com/rpc', {
|
|
775
|
-
'Authorization': 'Bearer your-token',
|
|
776
|
-
'X-API-Key': 'your-api-key',
|
|
777
|
-
'User-Agent': 'MyApp/1.0'
|
|
778
|
-
});
|
|
779
|
-
|
|
780
|
-
// Development client with self-signed certificate (accepts any certificate, DO NOT use in production)
|
|
781
|
-
const devClient = new RpcClient('https://localhost:3000/api', {
|
|
782
|
-
'Authorization': 'Bearer dev-token'
|
|
783
|
-
}, {
|
|
784
|
-
rejectUnauthorized: false // Development only!
|
|
785
|
-
});
|
|
786
|
-
|
|
787
|
-
// Development client with custom self-signed CA (safer: accepts only your CA)
|
|
788
|
-
const ca = fs.readFileSync('path/to/your/selfsigned-ca.pem');
|
|
789
|
-
const devClientWithCA = new RpcClient('https://localhost:3000/api', {
|
|
790
|
-
'Authorization': 'Bearer dev-token'
|
|
791
|
-
}, {
|
|
792
|
-
ca // Pass your self-signed CA certificate
|
|
793
|
-
// or: agent: new require('https').Agent({ ca, rejectUnauthorized: true })
|
|
794
|
-
});
|
|
795
|
-
```
|
|
796
|
-
|
|
797
|
-
```
|
|
798
|
-
|
|
799
|
-
**⚠️ Security Warning**: Use `rejectUnauthorized: false` for development only. For better security, always prefer the `ca` solution to accept only your self-signed CA. In production always use valid certificates signed by a trusted CA.
|
|
800
|
-
|
|
801
|
-
### Built-in Middlewares
|
|
802
|
-
|
|
803
|
-
```javascript
|
|
804
|
-
const { builtInMiddlewares } = require('rpc-express-toolkit');
|
|
805
|
-
|
|
806
|
-
// Rate limiting
|
|
807
|
-
rpc.use('beforeCall', builtInMiddlewares.rateLimit({
|
|
808
|
-
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
809
|
-
max: 100, // limit each IP to 100 requests per windowMs
|
|
810
|
-
message: 'Too many requests from this IP'
|
|
811
|
-
}));
|
|
812
|
-
|
|
813
|
-
// CORS
|
|
814
|
-
rpc.use('beforeCall', builtInMiddlewares.cors({
|
|
815
|
-
origin: ['http://localhost:3000', 'https://myapp.com'],
|
|
816
|
-
credentials: true,
|
|
817
|
-
methods: ['POST']
|
|
818
|
-
}));
|
|
819
|
-
|
|
820
|
-
// Authentication
|
|
821
|
-
rpc.use('beforeCall', builtInMiddlewares.auth({
|
|
822
|
-
required: true,
|
|
823
|
-
verify: async (req) => {
|
|
824
|
-
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
825
|
-
return await verifyJWT(token);
|
|
826
|
-
},
|
|
827
|
-
onError: (req, error) => {
|
|
828
|
-
console.log('Auth failed:', error.message);
|
|
829
|
-
}
|
|
830
|
-
}));
|
|
831
|
-
|
|
832
|
-
// Method whitelisting
|
|
833
|
-
rpc.use('beforeCall', builtInMiddlewares.methodWhitelist([
|
|
834
|
-
'add', 'subtract', 'getUser', 'updateUser'
|
|
835
|
-
]));
|
|
836
|
-
|
|
837
|
-
// Request timing
|
|
838
|
-
rpc.use('beforeCall', builtInMiddlewares.timing());
|
|
839
|
-
```
|
|
840
|
-
```
|
|
841
|
-
|
|
842
|
-
// Example token validation function (replace with your actual logic)
|
|
843
|
-
function isValidToken(token) {
|
|
844
|
-
// Simple example: Check if the token matches a predefined value
|
|
845
|
-
const expectedToken = 'my-secret-token'; // Replace with your actual token handling logic
|
|
846
|
-
return token === expectedToken;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
// Start the server
|
|
850
|
-
const PORT = process.env.PORT || 3000;
|
|
851
|
-
app.listen(PORT, () => {
|
|
852
|
-
console.log(`Server is running on port ${PORT}`);
|
|
853
|
-
});
|
|
854
|
-
```
|
|
855
|
-
|
|
856
|
-
### Adding Methods
|
|
857
|
-
|
|
858
|
-
You can add RPC methods using the `addMethod` function. Each method receives the `context` and `params` as arguments.
|
|
859
|
-
|
|
860
|
-
```javascript
|
|
861
|
-
// Synchronous method
|
|
862
|
-
rpc.addMethod('multiply', (req, ctx, params) => {
|
|
863
|
-
const { a, b } = params;
|
|
864
|
-
return a * b;
|
|
865
|
-
});
|
|
866
|
-
|
|
867
|
-
// Asynchronous method
|
|
868
|
-
rpc.addMethod('divide', async (req, ctx, params) => {
|
|
869
|
-
const { a, b } = params;
|
|
870
|
-
if (b === 0) {
|
|
871
|
-
throw new Error('Division by zero');
|
|
872
|
-
}
|
|
873
|
-
// Simulate async operation
|
|
874
|
-
return Promise.resolve(a / b);
|
|
875
|
-
});
|
|
876
|
-
```
|
|
877
|
-
|
|
878
|
-
## API
|
|
879
|
-
|
|
880
|
-
### RpcEndpoint Class
|
|
881
|
-
|
|
882
|
-
A class to handle JSON-RPC 2.0 requests in Express.js applications.
|
|
883
|
-
|
|
884
|
-
#### Constructor
|
|
885
|
-
|
|
886
|
-
```javascript
|
|
887
|
-
new RpcEndpoint(router, context, endpoint = '/api')
|
|
888
|
-
```
|
|
889
|
-
|
|
890
|
-
- **router**: *(Express.Router)* - The Express router to attach the endpoint to.
|
|
891
|
-
- **context**: *(Object)* - The context object to pass to the method handlers.
|
|
892
|
-
- **endpoint** *(optional)*: *(string)* - The endpoint path (default is `/api`).
|
|
893
|
-
|
|
894
|
-
#### addMethod
|
|
895
|
-
|
|
896
|
-
```javascript
|
|
897
|
-
addMethod(name, handler)
|
|
898
|
-
```
|
|
899
|
-
|
|
900
|
-
- **name**: *(string)* - The name of the RPC method.
|
|
901
|
-
- **handler**: *(Function)* - The function that handles the method. Receives `req` (the Request object) `context` and `params` as arguments.
|
|
902
|
-
|
|
903
|
-
```javascript
|
|
904
|
-
(req, context, params) => { /* ... */ }
|
|
905
|
-
```
|
|
906
|
-
|
|
907
|
-
#### Properties
|
|
908
|
-
|
|
909
|
-
- **endpoint**: *(string)* - Returns the endpoint path.
|
|
910
|
-
- **methods**: *(Object)* - Returns the registered methods.
|
|
911
|
-
|
|
912
|
-
#### reply
|
|
913
|
-
|
|
914
|
-
```javascript
|
|
915
|
-
reply(res, { id, result, error })
|
|
916
|
-
```
|
|
917
|
-
|
|
918
|
-
- **res**: *(Express.Response)* - The Express response object.
|
|
919
|
-
- **responsePayload**: *(Object)* - The JSON-RPC response payload.
|
|
920
|
-
|
|
921
|
-
```javascript
|
|
922
|
-
{
|
|
923
|
-
id: string | number | null,
|
|
924
|
-
result: any,
|
|
925
|
-
error: {
|
|
926
|
-
code: number,
|
|
927
|
-
message: string,
|
|
928
|
-
data?: any
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
```
|
|
932
|
-
|
|
933
|
-
#### serializeBigInts
|
|
934
|
-
|
|
935
|
-
```javascript
|
|
936
|
-
serializeBigInts(value)
|
|
39
|
+
const client = new RpcClient('http://localhost:3000/api');
|
|
40
|
+
const sum = await client.call('add', { a: 1, b: 2 });
|
|
937
41
|
```
|
|
938
42
|
|
|
939
|
-
|
|
940
|
-
- **returns**: *(any)* - The serialized value with `BigInt` converted to strings.
|
|
941
|
-
|
|
942
|
-
Recursively converts `BigInt` values to strings to ensure JSON serialization compatibility.
|
|
943
|
-
|
|
944
|
-
## Examples
|
|
945
|
-
|
|
946
|
-
Check out the [examples](./examples) directory for more usage examples.
|
|
947
|
-
|
|
948
|
-
### Example: Complete Express Server Integration
|
|
43
|
+
### Safe Type Disambiguation (optional)
|
|
949
44
|
|
|
950
|
-
|
|
951
|
-
const express = require('express');
|
|
952
|
-
const { RpcEndpoint } = require('rpc-express-toolkit');
|
|
45
|
+
Safe type disambiguation is **disabled by default** for maximum JSON-RPC 2.0 compatibility. The library will show educational warnings when you use BigInt or Date values, helping you understand when these types need special handling. Enable safe serialization with `safeEnabled: true` to add safe prefixes (`S:` for strings, `D:` for dates), or suppress warnings with `warnOnUnsafe: false`. See `README_ADVANCED.md#safe-serialization` for full details and best practices.
|
|
953
46
|
|
|
954
|
-
|
|
47
|
+
## Minimal API
|
|
955
48
|
|
|
956
|
-
|
|
49
|
+
- `new RpcEndpoint(router, context, options?)` — create and attach a JSON-RPC endpoint.
|
|
50
|
+
- `rpc.addMethod(name, handlerOrConfig)` — register a method.
|
|
51
|
+
- `new RpcClient(baseUrl, headers?, options?)` — client for making calls.
|
|
52
|
+
- `client.call(method, params?)` — single call.
|
|
53
|
+
- `client.batch([...])` — batch.
|
|
54
|
+
- `client.notify(method, params?)` — notification.
|
|
957
55
|
|
|
958
|
-
|
|
56
|
+
## Full Details
|
|
959
57
|
|
|
960
|
-
|
|
961
|
-
const rpc = new RpcEndpoint(app, context);
|
|
962
|
-
|
|
963
|
-
rpc.addMethod('greet', (req, ctx, params) => {
|
|
964
|
-
const { name } = params;
|
|
965
|
-
return `Hello, ${name}!`;
|
|
966
|
-
});
|
|
967
|
-
|
|
968
|
-
rpc.addMethod('getTime', () => {
|
|
969
|
-
return new Date().toISOString();
|
|
970
|
-
});
|
|
971
|
-
|
|
972
|
-
const PORT = 3000;
|
|
973
|
-
app.listen(PORT, () => {
|
|
974
|
-
console.log(`Server listening on port ${PORT}`);
|
|
975
|
-
});
|
|
976
|
-
```
|
|
58
|
+
For advanced configuration, middleware, structured logging, safe serialization, error handling, and more, see `README_ADVANCED.md`.
|
|
977
59
|
|
|
978
60
|
## Contributing
|
|
979
61
|
|
|
980
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
981
|
-
|
|
982
|
-
### Development Setup
|
|
983
|
-
|
|
984
|
-
1. Fork the repository
|
|
985
|
-
2. Clone your fork: `git clone https://github.com/yourusername/rpc-express-toolkit.git`
|
|
986
|
-
3. Install dependencies: `npm install`
|
|
987
|
-
4. Run tests: `npm test`
|
|
988
|
-
5. Run linting: `npm run lint`
|
|
989
|
-
|
|
990
|
-
### Running Tests
|
|
991
|
-
|
|
992
62
|
```bash
|
|
993
|
-
|
|
994
|
-
npm
|
|
995
|
-
npm
|
|
63
|
+
git clone https://github.com/n-car/rpc-express-toolkit.git
|
|
64
|
+
npm install
|
|
65
|
+
npm test
|
|
66
|
+
npm run lint
|
|
996
67
|
```
|
|
997
68
|
|
|
998
|
-
### Code Style
|
|
999
|
-
|
|
1000
|
-
This project uses ESLint and Prettier for code formatting. Run `npm run lint` to check code style.
|
|
1001
|
-
|
|
1002
|
-
## Security
|
|
1003
|
-
|
|
1004
|
-
### Best Practices
|
|
1005
|
-
|
|
1006
|
-
- Always validate input parameters using schema validation
|
|
1007
|
-
- Use authentication middleware for sensitive operations
|
|
1008
|
-
- Enable rate limiting to prevent abuse
|
|
1009
|
-
- Sanitize error messages in production environments
|
|
1010
|
-
- Use HTTPS in production
|
|
1011
|
-
- Implement proper logging for audit trails
|
|
1012
|
-
|
|
1013
|
-
## Performance
|
|
1014
|
-
|
|
1015
|
-
### Optimization Tips
|
|
1016
|
-
|
|
1017
|
-
- Use batch requests for multiple operations
|
|
1018
|
-
- Implement caching where appropriate
|
|
1019
|
-
- Use connection pooling for database operations
|
|
1020
|
-
- Monitor metrics and logs for performance bottlenecks
|
|
1021
|
-
- Use clustering for high-load applications
|
|
1022
|
-
|
|
1023
|
-
### Benchmarks
|
|
1024
|
-
|
|
1025
|
-
Performance benchmarks on a typical server setup:
|
|
1026
|
-
|
|
1027
|
-
- Single request: ~1-2ms overhead
|
|
1028
|
-
- Batch request (10 items): ~5-10ms total
|
|
1029
|
-
- Memory usage: ~50MB for 10,000 concurrent connections
|
|
1030
|
-
- Throughput: ~5,000 requests/second
|
|
1031
|
-
|
|
1032
69
|
## License
|
|
1033
70
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
## Changelog
|
|
1037
|
-
|
|
1038
|
-
### v2.0.0 (Enterprise Edition)
|
|
1039
|
-
|
|
1040
|
-
- ✨ **BREAKING**: Clean API with `RpcEndpoint` and `RpcClient` classes (100% backward compatible)
|
|
1041
|
-
- ✨ **BREAKING**: Renamed package to `rpc-express-toolkit` for better clarity
|
|
1042
|
-
- ✨ Added structured logging with configurable transports
|
|
1043
|
-
- ✨ Implemented middleware system with built-in middlewares
|
|
1044
|
-
- ✨ Added schema validation with Ajv and schema builder
|
|
1045
|
-
- ✨ Implemented batch request processing
|
|
1046
|
-
- ✨ Added health check and metrics endpoints
|
|
1047
|
-
- ✨ Enhanced BigInt/Date serialization with timezone support
|
|
1048
|
-
- ✨ Added authentication and authorization middleware
|
|
1049
|
-
- ✨ Implemented rate limiting and CORS support
|
|
1050
|
-
- ✨ Added method whitelisting and security features
|
|
1051
|
-
- ✨ Clean API: `RpcEndpoint`/`RpcClient` classes
|
|
1052
|
-
- 🔧 Improved error handling and sanitization
|
|
1053
|
-
- 🔧 Enhanced client with batch and notification support
|
|
1054
|
-
- 🔧 Added comprehensive test coverage
|
|
1055
|
-
- 📚 Updated documentation with enterprise features
|
|
1056
|
-
|
|
1057
|
-
## Support
|
|
1058
|
-
|
|
1059
|
-
- 📖 [Documentation](https://github.com/n-car/rpc-express-toolkit/wiki)
|
|
1060
|
-
- 🐛 [Issue Tracker](https://github.com/n-car/rpc-express-toolkit/issues)
|
|
1061
|
-
- 💬 [Discussions](https://github.com/n-car/rpc-express-toolkit/discussions)
|
|
1062
|
-
|
|
1063
|
-
## Acknowledgments
|
|
1064
|
-
|
|
1065
|
-
- JSON-RPC 2.0 Specification
|
|
1066
|
-
- Express.js community
|
|
1067
|
-
- Ajv JSON Schema validator
|
|
1068
|
-
- All contributors and users
|
|
1069
|
-
|
|
1070
|
-
---
|
|
1071
|
-
|
|
1072
|
-
Made with ❤️ by n-car
|
|
71
|
+
MIT. See [LICENSE](LICENSE).
|