allserver 1.2.0 → 1.3.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 +32 -8
- package/package.json +2 -1
- package/src/client/HttpClientTransport.js +3 -1
- package/src/index.js +3 -0
- package/src/server/ExpressTransport.js +74 -0
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ Superpowers the `Allserver` gives you:
|
|
|
12
12
|
- Run your HTTP server as gRPC with a single line change (almost).
|
|
13
13
|
- Serve same logic via HTTP and gRPC (or more) simultaneously in the same node.js process.
|
|
14
14
|
- Deploy and run your HTTP server on AWS Lambda with no code changes.
|
|
15
|
+
- Embed same exact code into your existing Express.js, no need to implement any handlers/middleware.
|
|
15
16
|
- Use Redis-backed job queue [BullMQ](https://docs.bullmq.io) to call remote procedures reliably.
|
|
16
17
|
- And moar!
|
|
17
18
|
|
|
@@ -143,15 +144,21 @@ Please note, that Allserver depends only on a single tiny npm module [`stampit`]
|
|
|
143
144
|
|
|
144
145
|
The default `HttpTransport` is using the [`micro`](http://npmjs.com/package/micro) npm module as an optional dependency.
|
|
145
146
|
|
|
146
|
-
```shell
|
|
147
|
+
```shell
|
|
147
148
|
npm i allserver micro
|
|
148
149
|
```
|
|
149
150
|
|
|
151
|
+
Alternatively, you can embed Allserver into your existing Express.js application:
|
|
152
|
+
|
|
153
|
+
```shell
|
|
154
|
+
npm i allserver express
|
|
155
|
+
```
|
|
156
|
+
|
|
150
157
|
#### Client
|
|
151
158
|
|
|
152
159
|
Optionally, you can use Allserver's built-in client:
|
|
153
160
|
|
|
154
|
-
```shell
|
|
161
|
+
```shell
|
|
155
162
|
npm i allserver node-fetch
|
|
156
163
|
```
|
|
157
164
|
|
|
@@ -163,7 +170,7 @@ Or do HTTP requests using any module you like.
|
|
|
163
170
|
|
|
164
171
|
No dependencies other than `allserver` itself.
|
|
165
172
|
|
|
166
|
-
```shell
|
|
173
|
+
```shell
|
|
167
174
|
npm i allserver
|
|
168
175
|
```
|
|
169
176
|
|
|
@@ -177,7 +184,7 @@ Same as the HTTP protocol client above.
|
|
|
177
184
|
|
|
178
185
|
The default `GrpcTransport` is using the standard the [`@grpc/grpc-js`](https://www.npmjs.com/package/@grpc/grpc-js) npm module as an optional dependency.
|
|
179
186
|
|
|
180
|
-
```shell
|
|
187
|
+
```shell
|
|
181
188
|
npm i allserver @grpc/grpc-js@1 @grpc/proto-loader@0.5
|
|
182
189
|
```
|
|
183
190
|
|
|
@@ -187,7 +194,7 @@ Note, with gRPC server and client you'd need to have your own `.proto` file. See
|
|
|
187
194
|
|
|
188
195
|
Optionally, you can use Allserver's built-in client:
|
|
189
196
|
|
|
190
|
-
```shell
|
|
197
|
+
```shell
|
|
191
198
|
npm i allserver @grpc/grpc-js@1 @grpc/proto-loader@0.5
|
|
192
199
|
```
|
|
193
200
|
|
|
@@ -199,7 +206,7 @@ Or do gRPC requests using any module you like.
|
|
|
199
206
|
|
|
200
207
|
The default `BullmqTransport` is using the [`bullmq`](https://www.npmjs.com/package/bullmq) module as a dependency, connects to Redis using `Worker` class.
|
|
201
208
|
|
|
202
|
-
```shell
|
|
209
|
+
```shell
|
|
203
210
|
npm i allserver bullmq
|
|
204
211
|
```
|
|
205
212
|
|
|
@@ -207,7 +214,7 @@ npm i allserver bullmq
|
|
|
207
214
|
|
|
208
215
|
Optionally, you can use Allserver's built-in client:
|
|
209
216
|
|
|
210
|
-
```shell
|
|
217
|
+
```shell
|
|
211
218
|
npm i allserver bullmq
|
|
212
219
|
```
|
|
213
220
|
|
|
@@ -332,6 +339,23 @@ const procedures = {
|
|
|
332
339
|
|
|
333
340
|
More info can be found in the [`micro`](http://npmjs.com/package/micro) NPM module docs.
|
|
334
341
|
|
|
342
|
+
### HTTP server in Express.js
|
|
343
|
+
|
|
344
|
+
Doesn't require a dedicated client transport. Use the HTTP client below.
|
|
345
|
+
|
|
346
|
+
NB: not yet tested in production.
|
|
347
|
+
|
|
348
|
+
```js
|
|
349
|
+
const { Allserver, ExpressTransport } = require("allserver");
|
|
350
|
+
|
|
351
|
+
const middleware = Allserver({
|
|
352
|
+
procedures,
|
|
353
|
+
transport: ExpressTransport(),
|
|
354
|
+
}).start();
|
|
355
|
+
|
|
356
|
+
app.use("/route-with-allsever", middleware);
|
|
357
|
+
```
|
|
358
|
+
|
|
335
359
|
### HTTP server in AWS Lambda
|
|
336
360
|
|
|
337
361
|
Doesn't require a dedicated client transport. Use the HTTP client below.
|
|
@@ -365,7 +389,7 @@ exports = Allserver({
|
|
|
365
389
|
|
|
366
390
|
You'd need to install `node-fetch` optional dependency.
|
|
367
391
|
|
|
368
|
-
```shell
|
|
392
|
+
```shell
|
|
369
393
|
npm i allserver node-fetch
|
|
370
394
|
```
|
|
371
395
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "allserver",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
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": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@grpc/proto-loader": "^0.5.5",
|
|
37
37
|
"bullmq": "^3.5.7",
|
|
38
38
|
"eslint": "^7.9.0",
|
|
39
|
+
"express": "^4.18.2",
|
|
39
40
|
"lambda-local": "^1.7.3",
|
|
40
41
|
"micro": "^9.3.4",
|
|
41
42
|
"mocha": "^10.2.0",
|
|
@@ -6,7 +6,9 @@ module.exports = require("./ClientTransport").compose({
|
|
|
6
6
|
props: {
|
|
7
7
|
// eslint-disable-next-line no-undef
|
|
8
8
|
fetch: (typeof self !== "undefined" && self.fetch) || require("node-fetch"),
|
|
9
|
-
headers: {
|
|
9
|
+
headers: {
|
|
10
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
11
|
+
},
|
|
10
12
|
},
|
|
11
13
|
|
|
12
14
|
init({ headers, fetch }) {
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const { parse: parseUrl } = require("url");
|
|
2
|
+
|
|
3
|
+
module.exports = require("./Transport").compose({
|
|
4
|
+
name: "ExpressTransport",
|
|
5
|
+
|
|
6
|
+
props: {
|
|
7
|
+
_express: require("express"),
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
methods: {
|
|
11
|
+
async deserializeRequest(ctx) {
|
|
12
|
+
if (ctx.http.req.deserializationFailed) return false;
|
|
13
|
+
// If there is no body we will use request query (aka search params)
|
|
14
|
+
let arg = ctx.http.query;
|
|
15
|
+
if (ctx.http.req.body && Object.keys(ctx.http.req.body).length > 0) arg = ctx.http.req.body;
|
|
16
|
+
ctx.arg = arg;
|
|
17
|
+
return true;
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
async _handleRequest(ctx) {
|
|
21
|
+
if (await this.deserializeRequest(ctx)) {
|
|
22
|
+
await ctx.allserver.handleCall(ctx);
|
|
23
|
+
} else {
|
|
24
|
+
// HTTP protocol request was malformed (not expected structure).
|
|
25
|
+
// We are not going to process it.
|
|
26
|
+
ctx.result = { success: false, code: "ALLSERVER_BAD_REQUEST", message: "Can't parse JSON" };
|
|
27
|
+
ctx.http.statusCode = 400;
|
|
28
|
+
this.reply(ctx);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
startServer(defaultCtx) {
|
|
33
|
+
return [
|
|
34
|
+
this._express.json(),
|
|
35
|
+
(err, req, res, next) => {
|
|
36
|
+
if (err.statusCode) req.deserializationFailed = err; // overriding the error, processing the request as normal
|
|
37
|
+
next();
|
|
38
|
+
},
|
|
39
|
+
async (req, res) => {
|
|
40
|
+
const ctx = {
|
|
41
|
+
...defaultCtx,
|
|
42
|
+
http: { req, res, query: req.query || {} },
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
await this._handleRequest(ctx);
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
getProcedureName(ctx) {
|
|
51
|
+
return parseUrl(ctx.http.req.url).pathname.substr(1);
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
isIntrospection(ctx) {
|
|
55
|
+
return this.getProcedureName(ctx) === "";
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
prepareNotFoundReply(ctx) {
|
|
59
|
+
ctx.http.statusCode = 404;
|
|
60
|
+
},
|
|
61
|
+
prepareProcedureErrorReply(ctx) {
|
|
62
|
+
// Generic exception.
|
|
63
|
+
ctx.http.statusCode = 500;
|
|
64
|
+
|
|
65
|
+
// nodejs assert() exception. In HTTP world this likely means 400 "Bad Request".
|
|
66
|
+
if (ctx.error.code === "ERR_ASSERTION") ctx.http.statusCode = 400;
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
reply(ctx) {
|
|
70
|
+
if (!ctx.http.statusCode) ctx.http.statusCode = 200;
|
|
71
|
+
ctx.http.res.status(ctx.http.statusCode).json(ctx.result);
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
});
|