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 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 available. E.g. you can't have `/users/updateUser`. (Yet.)
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({ schema: "unixsocket", Transport: MyUnixSocketTransport });
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({ fetch: require("./fetch-retry") });
509
- AllserverClient = AllserverClient.addTransport({ schema: "http", Transport: HttpClientTransport });
510
- AllserverClient = AllserverClient.addTransport({ schema: "https", Transport: HttpClientTransport });
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 can have `before` and `after` middleware(s) which run client-side.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "allserver",
3
- "version": "0.0.22",
3
+ "version": "1.0.1",
4
4
  "description": "Multi-protocol simple RPC server and [optional] client. Boilerplate-less. Opinionated. Minimalistic. DX-first.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -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
- this.logger.error("ALLSERVER_PROCEDURE_ERROR", err);
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: err.code || "ALLSERVER_PROCEDURE_ERROR",
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
- this.logger.error("ALLSERVER_MIDDLEWARE_ERROR", err);
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: err.code || "ALLSERVER_MIDDLEWARE_ERROR",
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
- _micro: require("micro"),
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._micro.buffer(ctx.http.req);
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._micro.json(ctx.http.req);
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._micro(async (req, res) => {
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._micro.send(ctx.http.res, ctx.http.statusCode, ctx.result);
81
+ this.micro.send(ctx.http.res, ctx.http.statusCode, ctx.result);
82
82
  },
83
83
  },
84
84
  });