0
# RPC Services
1
2
Built-in RPC service framework for implementing and consuming protobuf-based services with support for both traditional and streaming RPC patterns.
3
4
## Capabilities
5
6
### Service Definition
7
8
Service reflection and definition management for RPC services.
9
10
```javascript { .api }
11
class Service extends Namespace {
12
/**
13
* Constructs a new service
14
* @param name - Service name
15
* @param options - Service options
16
*/
17
constructor(name: string, options?: object);
18
19
/**
20
* Creates service from JSON descriptor
21
* @param name - Service name
22
* @param json - JSON service descriptor
23
* @returns Service instance
24
*/
25
static fromJSON(name: string, json: object): Service;
26
27
/**
28
* Service methods by name
29
*/
30
methods: { [name: string]: Method };
31
32
/**
33
* Array of all methods
34
*/
35
methodsArray: Method[];
36
37
/**
38
* Creates RPC service instance
39
* @param rpcImpl - RPC implementation function
40
* @param requestDelimited - Whether requests are length-delimited
41
* @param responseDelimited - Whether responses are length-delimited
42
* @returns RPC service instance
43
*/
44
create(rpcImpl: RPCImpl, requestDelimited?: boolean, responseDelimited?: boolean): rpc.Service;
45
46
/**
47
* Adds method to service
48
* @param method - Method to add
49
* @returns This service
50
*/
51
add(method: Method): Service;
52
53
/**
54
* Removes method from service
55
* @param method - Method to remove
56
* @returns This service
57
*/
58
remove(method: Method): Service;
59
}
60
```
61
62
**Usage Examples:**
63
64
```javascript
65
const protobuf = require("protobufjs");
66
67
protobuf.load("service.proto", function(err, root) {
68
const UserService = root.lookupService("UserService");
69
70
console.log("Service methods:");
71
UserService.methodsArray.forEach(method => {
72
console.log(`${method.name}: ${method.requestType} -> ${method.responseType}`);
73
});
74
75
// Create service instance
76
const service = UserService.create(rpcImplementation);
77
});
78
```
79
80
### Method Definition
81
82
RPC method reflection and metadata.
83
84
```javascript { .api }
85
class Method extends ReflectionObject {
86
/**
87
* Constructs a new method
88
* @param name - Method name
89
* @param type - Method type (typically "rpc")
90
* @param requestType - Request message type name
91
* @param responseType - Response message type name
92
* @param requestStream - Whether request is streaming
93
* @param responseStream - Whether response is streaming
94
* @param options - Method options
95
*/
96
constructor(name: string, type: string, requestType: string, responseType: string,
97
requestStream?: boolean, responseStream?: boolean, options?: object);
98
99
/**
100
* Method type (typically "rpc")
101
*/
102
type: string;
103
104
/**
105
* Request message type name
106
*/
107
requestType: string;
108
109
/**
110
* Response message type name
111
*/
112
responseType: string;
113
114
/**
115
* Whether request is streaming
116
*/
117
requestStream: boolean;
118
119
/**
120
* Whether response is streaming
121
*/
122
responseStream: boolean;
123
124
/**
125
* Resolved request message type
126
*/
127
resolvedRequestType: Type | null;
128
129
/**
130
* Resolved response message type
131
*/
132
resolvedResponseType: Type | null;
133
}
134
```
135
136
**Usage Examples:**
137
138
```javascript
139
// Inspect method details
140
const getUserMethod = UserService.methods["getUser"];
141
console.log("Method:", getUserMethod.name);
142
console.log("Request type:", getUserMethod.requestType);
143
console.log("Response type:", getUserMethod.responseType);
144
console.log("Streaming:", {
145
request: getUserMethod.requestStream,
146
response: getUserMethod.responseStream
147
});
148
149
// Access resolved types
150
const RequestType = getUserMethod.resolvedRequestType;
151
const ResponseType = getUserMethod.resolvedResponseType;
152
```
153
154
### RPC Implementation Interface
155
156
Interface for implementing RPC transport and execution.
157
158
```javascript { .api }
159
/**
160
* RPC implementation function
161
* @param method - Method being called
162
* @param requestData - Encoded request data
163
* @param callback - Callback for response
164
*/
165
interface RPCImpl {
166
(method: Method, requestData: Uint8Array, callback: RPCImplCallback): void;
167
}
168
169
/**
170
* RPC implementation callback
171
* @param error - Error if call failed, null on success
172
* @param response - Encoded response data if successful
173
*/
174
interface RPCImplCallback {
175
(error: Error | null, response?: Uint8Array): void;
176
}
177
```
178
179
**Usage Examples:**
180
181
```javascript
182
// HTTP-based RPC implementation
183
function httpRpcImpl(method, requestData, callback) {
184
const xhr = new XMLHttpRequest();
185
xhr.open("POST", `/rpc/${method.name}`);
186
xhr.responseType = "arraybuffer";
187
188
xhr.onload = function() {
189
if (xhr.status === 200) {
190
const responseBuffer = new Uint8Array(xhr.response);
191
callback(null, responseBuffer);
192
} else {
193
callback(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`));
194
}
195
};
196
197
xhr.onerror = function() {
198
callback(new Error("Network error"));
199
};
200
201
xhr.send(requestData);
202
}
203
204
// WebSocket-based RPC implementation
205
function websocketRpcImpl(method, requestData, callback) {
206
const message = {
207
method: method.name,
208
data: Array.from(requestData)
209
};
210
211
websocket.send(JSON.stringify(message));
212
213
// Store callback for response matching
214
pendingCalls[generateId()] = callback;
215
}
216
217
// Node.js HTTP client implementation
218
function nodeHttpRpcImpl(method, requestData, callback) {
219
const options = {
220
hostname: 'localhost',
221
port: 8080,
222
path: `/rpc/${method.name}`,
223
method: 'POST',
224
headers: {
225
'Content-Type': 'application/x-protobuf',
226
'Content-Length': requestData.length
227
}
228
};
229
230
const req = require('http').request(options, (res) => {
231
const chunks = [];
232
res.on('data', chunk => chunks.push(chunk));
233
res.on('end', () => {
234
const responseBuffer = Buffer.concat(chunks);
235
callback(null, new Uint8Array(responseBuffer));
236
});
237
});
238
239
req.on('error', callback);
240
req.write(requestData);
241
req.end();
242
}
243
```
244
245
### Service Instance
246
247
Runtime service instance for making RPC calls.
248
249
```javascript { .api }
250
namespace rpc {
251
class Service {
252
/**
253
* RPC implementation
254
*/
255
rpcImpl: RPCImpl;
256
257
/**
258
* Whether requests are length-delimited
259
*/
260
requestDelimited: boolean;
261
262
/**
263
* Whether responses are length-delimited
264
*/
265
responseDelimited: boolean;
266
267
/**
268
* Creates service method wrapper
269
* @param method - Method definition
270
* @param requestCtor - Request message constructor
271
* @param responseCtor - Response message constructor
272
* @returns Method wrapper function
273
*/
274
rpcCall<TRequest, TResponse>(
275
method: Method,
276
requestCtor: Constructor<TRequest>,
277
responseCtor: Constructor<TResponse>
278
): (request: TRequest, callback: ServiceMethodCallback<TResponse>) => void;
279
}
280
}
281
282
/**
283
* Service method callback
284
* @param error - Error if call failed
285
* @param response - Response message if successful
286
*/
287
interface ServiceMethodCallback<TResponse> {
288
(error: Error | null, response?: TResponse): void;
289
}
290
```
291
292
**Usage Examples:**
293
294
```javascript
295
// Create and use service instance
296
const userService = UserService.create(httpRpcImpl);
297
298
// Make RPC calls
299
const getUserRequest = { userId: 123 };
300
userService.getUser(getUserRequest, function(err, response) {
301
if (err) {
302
console.error("RPC error:", err);
303
return;
304
}
305
306
console.log("User:", response.name, response.email);
307
});
308
309
// Promise-based wrapper
310
function promisifyService(service) {
311
const promisified = {};
312
313
Object.keys(service).forEach(methodName => {
314
if (typeof service[methodName] === 'function') {
315
promisified[methodName] = function(request) {
316
return new Promise((resolve, reject) => {
317
service[methodName](request, (err, response) => {
318
if (err) reject(err);
319
else resolve(response);
320
});
321
});
322
};
323
}
324
});
325
326
return promisified;
327
}
328
329
// Use promisified service
330
const promisedUserService = promisifyService(userService);
331
promisedUserService.getUser({ userId: 123 })
332
.then(user => console.log("User:", user))
333
.catch(err => console.error("Error:", err));
334
```
335
336
### Streaming RPC Support
337
338
Support for streaming RPC patterns (client streaming, server streaming, bidirectional streaming).
339
340
```javascript { .api }
341
interface StreamingPatterns {
342
/**
343
* Unary RPC (single request, single response)
344
*/
345
unary: {
346
requestStream: false;
347
responseStream: false;
348
};
349
350
/**
351
* Client streaming (multiple requests, single response)
352
*/
353
clientStreaming: {
354
requestStream: true;
355
responseStream: false;
356
};
357
358
/**
359
* Server streaming (single request, multiple responses)
360
*/
361
serverStreaming: {
362
requestStream: false;
363
responseStream: true;
364
};
365
366
/**
367
* Bidirectional streaming (multiple requests, multiple responses)
368
*/
369
bidirectionalStreaming: {
370
requestStream: true;
371
responseStream: true;
372
};
373
}
374
```
375
376
**Usage Examples:**
377
378
```javascript
379
// Server streaming implementation
380
function serverStreamingRpcImpl(method, requestData, callback) {
381
if (method.responseStream) {
382
// Set up streaming response handler
383
const stream = new EventEmitter();
384
385
stream.on('data', (responseData) => {
386
// Each response chunk
387
callback(null, responseData);
388
});
389
390
stream.on('end', () => {
391
// Signal end of stream
392
callback(null, null);
393
});
394
395
stream.on('error', (err) => {
396
callback(err);
397
});
398
399
// Initiate streaming call
400
initiateStreamingCall(method.name, requestData, stream);
401
} else {
402
// Regular unary call
403
regularRpcImpl(method, requestData, callback);
404
}
405
}
406
407
// Client streaming implementation
408
function clientStreamingService(method, requestStream) {
409
return new Promise((resolve, reject) => {
410
const chunks = [];
411
412
requestStream.on('data', (requestData) => {
413
chunks.push(requestData);
414
});
415
416
requestStream.on('end', () => {
417
// Combine all request chunks
418
const combinedRequest = Buffer.concat(chunks);
419
420
// Make single RPC call with combined data
421
rpcImpl(method, combinedRequest, (err, response) => {
422
if (err) reject(err);
423
else resolve(response);
424
});
425
});
426
});
427
}
428
```
429
430
### Service Registration
431
432
Utilities for registering and discovering RPC services.
433
434
```javascript { .api }
435
interface ServiceRegistry {
436
/**
437
* Registers service implementation
438
* @param serviceName - Service name
439
* @param implementation - Service implementation
440
*/
441
register(serviceName: string, implementation: object): void;
442
443
/**
444
* Gets registered service
445
* @param serviceName - Service name
446
* @returns Service implementation
447
*/
448
get(serviceName: string): object | null;
449
450
/**
451
* Lists all registered services
452
* @returns Array of service names
453
*/
454
list(): string[];
455
}
456
```
457
458
**Usage Examples:**
459
460
```javascript
461
// Service implementation
462
const userServiceImpl = {
463
getUser: function(request, callback) {
464
// Implementation logic
465
const user = database.findUser(request.userId);
466
const response = UserResponse.create({
467
id: user.id,
468
name: user.name,
469
email: user.email
470
});
471
callback(null, response);
472
},
473
474
createUser: function(request, callback) {
475
// Implementation logic
476
const newUser = database.createUser(request);
477
const response = UserResponse.create(newUser);
478
callback(null, response);
479
}
480
};
481
482
// Register service
483
serviceRegistry.register("UserService", userServiceImpl);
484
485
// Generic RPC handler
486
function handleRpcCall(serviceName, methodName, requestData, callback) {
487
const service = serviceRegistry.get(serviceName);
488
if (!service) {
489
return callback(new Error(`Service not found: ${serviceName}`));
490
}
491
492
const method = service[methodName];
493
if (!method) {
494
return callback(new Error(`Method not found: ${methodName}`));
495
}
496
497
// Decode request and call method
498
const ServiceDef = root.lookupService(serviceName);
499
const MethodDef = ServiceDef.methods[methodName];
500
const RequestType = MethodDef.resolvedRequestType;
501
502
const request = RequestType.decode(requestData);
503
method(request, callback);
504
}
505
```
506
507
### Error Handling
508
509
Comprehensive error handling for RPC operations.
510
511
```javascript { .api }
512
interface RPCError extends Error {
513
name: string; // Error type
514
message: string; // Error description
515
code?: number; // Error code
516
details?: any; // Additional error details
517
method?: string; // Method that failed
518
}
519
520
interface RPCErrorCodes {
521
OK: 0;
522
CANCELLED: 1;
523
UNKNOWN: 2;
524
INVALID_ARGUMENT: 3;
525
DEADLINE_EXCEEDED: 4;
526
NOT_FOUND: 5;
527
ALREADY_EXISTS: 6;
528
PERMISSION_DENIED: 7;
529
UNAUTHENTICATED: 16;
530
RESOURCE_EXHAUSTED: 8;
531
FAILED_PRECONDITION: 9;
532
ABORTED: 10;
533
OUT_OF_RANGE: 11;
534
UNIMPLEMENTED: 12;
535
INTERNAL: 13;
536
UNAVAILABLE: 14;
537
DATA_LOSS: 15;
538
}
539
```
540
541
**Usage Examples:**
542
543
```javascript
544
// Enhanced error handling in RPC implementation
545
function robustRpcImpl(method, requestData, callback) {
546
try {
547
// Validate method
548
if (!method) {
549
const error = new Error("Method not specified");
550
error.code = RPCErrorCodes.INVALID_ARGUMENT;
551
return callback(error);
552
}
553
554
// Make RPC call with timeout
555
const timeoutId = setTimeout(() => {
556
const error = new Error("RPC call timed out");
557
error.code = RPCErrorCodes.DEADLINE_EXCEEDED;
558
error.method = method.name;
559
callback(error);
560
}, 30000);
561
562
makeRpcCall(method, requestData, (err, response) => {
563
clearTimeout(timeoutId);
564
565
if (err) {
566
// Enhance error with context
567
err.method = method.name;
568
err.code = err.code || RPCErrorCodes.UNKNOWN;
569
}
570
571
callback(err, response);
572
});
573
574
} catch (err) {
575
err.code = RPCErrorCodes.INTERNAL;
576
err.method = method.name;
577
callback(err);
578
}
579
}
580
581
// Error handling in service calls
582
userService.getUser(request, function(err, response) {
583
if (err) {
584
switch (err.code) {
585
case RPCErrorCodes.NOT_FOUND:
586
console.log("User not found");
587
break;
588
case RPCErrorCodes.PERMISSION_DENIED:
589
console.log("Access denied");
590
break;
591
case RPCErrorCodes.UNAVAILABLE:
592
console.log("Service unavailable, retrying...");
593
// Implement retry logic
594
break;
595
default:
596
console.error("RPC error:", err.message);
597
}
598
return;
599
}
600
601
// Success
602
console.log("User retrieved:", response);
603
});
604
```
605
606
## Types
607
608
```javascript { .api }
609
interface RPCImpl {
610
(method: Method, requestData: Uint8Array, callback: RPCImplCallback): void;
611
}
612
613
interface RPCImplCallback {
614
(error: Error | null, response?: Uint8Array): void;
615
}
616
617
interface ServiceMethodCallback<TResponse> {
618
(error: Error | null, response?: TResponse): void;
619
}
620
621
interface Constructor<T> extends Function {
622
new (...args: any[]): T;
623
}
624
```