allserver 0.0.22 → 1.0.1
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 +64 -15
- package/package.json +1 -1
- package/src/server/Allserver.js +6 -4
- package/src/server/HttpTransport.js +5 -5
package/README.md
CHANGED
|
@@ -272,6 +272,44 @@ Allserver({
|
|
|
272
272
|
}).start();
|
|
273
273
|
```
|
|
274
274
|
|
|
275
|
+
#### Replying non-standard HTTP response from a procedure
|
|
276
|
+
|
|
277
|
+
You'd need to deal with node.js `res` yourself.
|
|
278
|
+
|
|
279
|
+
```js
|
|
280
|
+
const procedures = {
|
|
281
|
+
processEntity({ someEntity }, ctx) {
|
|
282
|
+
const res = ctx.http.res;
|
|
283
|
+
res.statusCode = 422;
|
|
284
|
+
const msg = "Unprocessable Entity";
|
|
285
|
+
res.setHeader("Content-Length", Buffer.byteLength(msg));
|
|
286
|
+
res.send(msg);
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
#### Accessing Node request and its raw body
|
|
292
|
+
|
|
293
|
+
Occasionally, your HTTP method would need to access raw body of a request. This is how you do it:
|
|
294
|
+
|
|
295
|
+
```js
|
|
296
|
+
const procedures = {
|
|
297
|
+
async processEntity(_, ctx) {
|
|
298
|
+
const micro = ctx.allserver.transport.micro; // same as require("micro")
|
|
299
|
+
const req = ctx.http.req; // node.js Request
|
|
300
|
+
|
|
301
|
+
// as a string
|
|
302
|
+
const text = await micro.text(req);
|
|
303
|
+
// as a node.js buffer
|
|
304
|
+
const buffer = await micro.buffer(req);
|
|
305
|
+
|
|
306
|
+
// ... process the request here ...
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
More info can be found in the [`micro`](http://npmjs.com/package/micro) NPM module docs.
|
|
312
|
+
|
|
275
313
|
### HTTP server in AWS Lambda
|
|
276
314
|
|
|
277
315
|
Doesn't require a dedicated client transport. Use the HTTP client below.
|
|
@@ -359,7 +397,7 @@ Yeah. This is a mutating call using `HTTP GET`. That's by design, and I love it.
|
|
|
359
397
|
|
|
360
398
|
### HTTP limitations
|
|
361
399
|
|
|
362
|
-
1. Sub-routes are not
|
|
400
|
+
1. Sub-routes are not well-supported, your procedure should be named `"users/updateUser"` or alike. Also, in the `AllserverClient` you'd want to use `nameMapper`.
|
|
363
401
|
|
|
364
402
|
### gRPC server side
|
|
365
403
|
|
|
@@ -446,31 +484,31 @@ const { success, code, message, user } = data;
|
|
|
446
484
|
**All the arguments are optional.** But either `uri` or `transport` must be provided. We are trying to keep the highest possible DX here.
|
|
447
485
|
|
|
448
486
|
- `uri`<br>
|
|
449
|
-
The remote server address string. Out of box supported schemas are: `http`, `https`, `grpc`. (More to come.)
|
|
487
|
+
The remote server address string. Out of box supported schemas are: `http`, `https`, `grpc`. (More to come.)
|
|
450
488
|
|
|
451
489
|
- `transport`<br>
|
|
452
|
-
The transport implementation object. The `uri` is ignored if this option provided. If not given then it will be automatically created based on the `uri` schema. E.g. if it starts with `http://` or `https:/` then `HttpClientTransport` will be used. If starts with `grpc://` then `GrpcClientTransport` will be used.
|
|
490
|
+
The transport implementation object. The `uri` is ignored if this option provided. If not given then it will be automatically created based on the `uri` schema. E.g. if it starts with `http://` or `https:/` then `HttpClientTransport` will be used. If starts with `grpc://` then `GrpcClientTransport` will be used.
|
|
453
491
|
|
|
454
492
|
- `neverThrow=true`<br>
|
|
455
|
-
Set it to `false` if you want to get exceptions when there are a network, or a server errors during a procedure call. Otherwise, the standard `{success,code,message}` object is returned from method calls. The Allserver error `code`s are always start with `"ALLSERVER_"`. E.g. `"ALLSERVER_CLIENT_MALFORMED_INTROSPECTION"`.
|
|
493
|
+
Set it to `false` if you want to get exceptions when there are a network, or a server errors during a procedure call. Otherwise, the standard `{success,code,message}` object is returned from method calls. The Allserver error `code`s are always start with `"ALLSERVER_"`. E.g. `"ALLSERVER_CLIENT_MALFORMED_INTROSPECTION"`.
|
|
456
494
|
|
|
457
495
|
- `dynamicMethods=true`<br>
|
|
458
|
-
Automatically find (introspect) and call corresponding remote procedures. If set to `false` the `AllserverClient` would use only the `methods` you defined explicitly client-side.
|
|
496
|
+
Automatically find (introspect) and call corresponding remote procedures. If set to `false` the `AllserverClient` would use only the `methods` you defined explicitly client-side.
|
|
459
497
|
|
|
460
498
|
- `autoIntrospect=true`<br>
|
|
461
|
-
Do not automatically search (introspect) for remote procedures, instead use the runtime method names. This mean `AllserverClient` won't guarantee the procedure existence until you try calling the procedure. E.g., this code `allserverClient.myProcedureName()` will do `POST /myProcedureName` HTTP request (aka "call of faith"). Useful when you don't want to expose introspection HTTP endpoint or don't want to add Allserver's mandatory proto in GRPC server.
|
|
499
|
+
Do not automatically search (introspect) for remote procedures, instead use the runtime method names. This mean `AllserverClient` won't guarantee the procedure existence until you try calling the procedure. E.g., this code `allserverClient.myProcedureName()` will do `POST /myProcedureName` HTTP request (aka "call of faith"). Useful when you don't want to expose introspection HTTP endpoint or don't want to add Allserver's mandatory proto in GRPC server.
|
|
462
500
|
|
|
463
501
|
- `callIntrospectedProceduresOnly=true`<br>
|
|
464
|
-
If introspection couldn't find a procedure then do not attempt sending a "call of faith" to the server.
|
|
502
|
+
If introspection couldn't find a procedure then do not attempt sending a "call of faith" to the server.
|
|
465
503
|
|
|
466
504
|
- `nameMapper`<br>
|
|
467
|
-
A function to map/filter procedure names found on the server to something else. E.g. `nameMapper: name => _.toCamelCase(name)`. If "falsy" value is returned from `nameMapper()` then this procedure won't be added to the `AllserverClient` object instance, like if it was not found on the server.
|
|
505
|
+
A function to map/filter procedure names found on the server to something else. E.g. `nameMapper: name => _.toCamelCase(name)`. If "falsy" value is returned from `nameMapper()` then this procedure won't be added to the `AllserverClient` object instance, like if it was not found on the server.
|
|
468
506
|
|
|
469
507
|
- `before`<br>
|
|
470
|
-
The "before" client-side middleware(s). Can be either a function, or an array of functions.
|
|
508
|
+
The "before" client-side middleware(s). Can be either a function, or an array of functions.
|
|
471
509
|
|
|
472
510
|
- `after`<br>
|
|
473
|
-
The "after" client-side middleware(s). Can be either a function, or an array of functions.
|
|
511
|
+
The "after" client-side middleware(s). Can be either a function, or an array of functions.
|
|
474
512
|
|
|
475
513
|
### AllserverClient defaults
|
|
476
514
|
|
|
@@ -497,7 +535,10 @@ const httpClient = AllserverClient({ uri: "https://example.com" });
|
|
|
497
535
|
You can add your own schema support to AllserverClient.
|
|
498
536
|
|
|
499
537
|
```js
|
|
500
|
-
AllserverClient = AllserverClient.addTransport({
|
|
538
|
+
AllserverClient = AllserverClient.addTransport({
|
|
539
|
+
schema: "unixsocket",
|
|
540
|
+
Transport: MyUnixSocketTransport,
|
|
541
|
+
});
|
|
501
542
|
|
|
502
543
|
const client = AllserverClient({ uri: "unixsocket:///example/socket" });
|
|
503
544
|
```
|
|
@@ -505,9 +546,17 @@ const client = AllserverClient({ uri: "unixsocket:///example/socket" });
|
|
|
505
546
|
You can overwrite the default client transport implementations:
|
|
506
547
|
|
|
507
548
|
```js
|
|
508
|
-
HttpClientTransport = HttpClientTransport.props({
|
|
509
|
-
|
|
510
|
-
|
|
549
|
+
HttpClientTransport = HttpClientTransport.props({
|
|
550
|
+
fetch: require("./fetch-retry"),
|
|
551
|
+
});
|
|
552
|
+
AllserverClient = AllserverClient.addTransport({
|
|
553
|
+
schema: "http",
|
|
554
|
+
Transport: HttpClientTransport,
|
|
555
|
+
});
|
|
556
|
+
AllserverClient = AllserverClient.addTransport({
|
|
557
|
+
schema: "https",
|
|
558
|
+
Transport: HttpClientTransport,
|
|
559
|
+
});
|
|
511
560
|
```
|
|
512
561
|
|
|
513
562
|
## FAQ
|
|
@@ -657,7 +706,7 @@ const client = AllserverClient({
|
|
|
657
706
|
|
|
658
707
|
##### My authorisation is not supported. What should I do?
|
|
659
708
|
|
|
660
|
-
If something more sophisticated is needed - you
|
|
709
|
+
If something more sophisticated is needed - you would need to mangle the `ctx` in the client `before` and `after` middlewares.
|
|
661
710
|
|
|
662
711
|
```js
|
|
663
712
|
const { AllserverClient } = require("allserver");
|
package/package.json
CHANGED
package/src/server/Allserver.js
CHANGED
|
@@ -65,11 +65,12 @@ module.exports = require("stampit")({
|
|
|
65
65
|
try {
|
|
66
66
|
result = await ctx.procedure(ctx.arg, ctx);
|
|
67
67
|
} catch (err) {
|
|
68
|
-
|
|
68
|
+
const code = err.code || "ALLSERVER_PROCEDURE_ERROR"
|
|
69
|
+
this.logger.error(code, err);
|
|
69
70
|
ctx.error = err;
|
|
70
71
|
ctx.result = {
|
|
71
72
|
success: false,
|
|
72
|
-
code
|
|
73
|
+
code,
|
|
73
74
|
message: `'${err.message}' error in '${ctx.procedureName}' procedure`,
|
|
74
75
|
};
|
|
75
76
|
await this.transport.prepareProcedureErrorReply(ctx);
|
|
@@ -102,11 +103,12 @@ module.exports = require("stampit")({
|
|
|
102
103
|
break;
|
|
103
104
|
}
|
|
104
105
|
} catch (err) {
|
|
105
|
-
|
|
106
|
+
const code = err.code || "ALLSERVER_MIDDLEWARE_ERROR";
|
|
107
|
+
this.logger.error(code, err);
|
|
106
108
|
ctx.error = err;
|
|
107
109
|
ctx.result = {
|
|
108
110
|
success: false,
|
|
109
|
-
code
|
|
111
|
+
code,
|
|
110
112
|
message: `'${err.message}' error in '${middlewareType}' middleware`,
|
|
111
113
|
};
|
|
112
114
|
return;
|
|
@@ -4,7 +4,7 @@ module.exports = require("./Transport").compose({
|
|
|
4
4
|
name: "HttpTransport",
|
|
5
5
|
|
|
6
6
|
props: {
|
|
7
|
-
|
|
7
|
+
micro: require("micro"),
|
|
8
8
|
port: process.env.PORT,
|
|
9
9
|
},
|
|
10
10
|
|
|
@@ -14,11 +14,11 @@ module.exports = require("./Transport").compose({
|
|
|
14
14
|
|
|
15
15
|
methods: {
|
|
16
16
|
async _deserializeRequest(ctx) {
|
|
17
|
-
const bodyBuffer = await this.
|
|
17
|
+
const bodyBuffer = await this.micro.buffer(ctx.http.req);
|
|
18
18
|
let arg = ctx.http.query;
|
|
19
19
|
try {
|
|
20
20
|
// If there is no body we will use request query (aka search params)
|
|
21
|
-
if (bodyBuffer.length !== 0) arg = await this.
|
|
21
|
+
if (bodyBuffer.length !== 0) arg = await this.micro.json(ctx.http.req);
|
|
22
22
|
ctx.arg = arg;
|
|
23
23
|
return true;
|
|
24
24
|
} catch (err) {
|
|
@@ -39,7 +39,7 @@ module.exports = require("./Transport").compose({
|
|
|
39
39
|
},
|
|
40
40
|
|
|
41
41
|
startServer(defaultCtx) {
|
|
42
|
-
this.server = this.
|
|
42
|
+
this.server = this.micro(async (req, res) => {
|
|
43
43
|
const ctx = { ...defaultCtx, http: { req, res, url: parseUrl(req.url) } };
|
|
44
44
|
|
|
45
45
|
ctx.http.query = {};
|
|
@@ -78,7 +78,7 @@ module.exports = require("./Transport").compose({
|
|
|
78
78
|
|
|
79
79
|
reply(ctx) {
|
|
80
80
|
if (!ctx.http.statusCode) ctx.http.statusCode = 200;
|
|
81
|
-
this.
|
|
81
|
+
this.micro.send(ctx.http.res, ctx.http.statusCode, ctx.result);
|
|
82
82
|
},
|
|
83
83
|
},
|
|
84
84
|
});
|