ultimate-express 1.2.1 → 1.2.3
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 +29 -2
- package/package.json +1 -1
- package/src/middlewares.js +63 -0
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ To make sure µExpress matches behavior of Express in all cases, we run all test
|
|
|
16
16
|
|
|
17
17
|
Similar projects based on uWebSockets:
|
|
18
18
|
|
|
19
|
-
- `express` on Bun - since Bun uses uWS for its HTTP module, Express is about 2
|
|
19
|
+
- `express` on Bun - since Bun uses uWS for its HTTP module, Express is about 2-3 times faster than on Node.js, but still slower than µExpress because it doesn't do uWS-specific optimizations.
|
|
20
20
|
- `hyper-express` - while having a similar API to Express, it's very far from being a drop-in replacement, and implements most of the functionality differently. This creates a lot of random quirks and issues, making the switch quite difficult. Built in middlewares are also very different.
|
|
21
21
|
- `uwebsockets-express` - this library is closer to being a drop-in replacement, but misses a lot of APIs, depends on Express by calling it's methods under the hood and doesn't try to optimize routing by using native uWS router.
|
|
22
22
|
|
|
@@ -24,6 +24,8 @@ Similar projects based on uWebSockets:
|
|
|
24
24
|
|
|
25
25
|
Tested using [wrk](https://github.com/wg/wrk) (`-d 60 -t 1 -c 200`). Etag was disabled in both Express and µExpress. Tested on Ubuntu 22.04, Node.js 20.17.0, AMD Ryzen 5 3600, 64GB RAM.
|
|
26
26
|
|
|
27
|
+
### Test results
|
|
28
|
+
|
|
27
29
|
| Test | Express req/sec | µExpress req/sec | Express throughput | µExpress throughput | µExpress speedup |
|
|
28
30
|
| --------------------------------------------- | --------------- | ---------------- | ------------------ | ------------------- | ---------------- |
|
|
29
31
|
| routing/simple-routes (/) | 10.90k | 70.10k | 2.04 MB/sec | 11.57 MB/sec | **6.43X** |
|
|
@@ -34,6 +36,30 @@ Tested using [wrk](https://github.com/wg/wrk) (`-d 60 -t 1 -c 200`). Etag was di
|
|
|
34
36
|
| engines/ejs (/test) | 5.92k | 41.64k | 2.40 MB/sec | 16.55 MB/sec | **7.03X** |
|
|
35
37
|
| middlewares/body-urlencoded (/abc) | 7.90k | 29.90k | 1.64 MB/sec | 5.36 MB/sec | **3.78X** |
|
|
36
38
|
|
|
39
|
+
### Performance against other frameworks
|
|
40
|
+
|
|
41
|
+
Tested using [bun-http-framework-benchmark](https://github.com/dimdenGD/bun-http-framework-benchmark)
|
|
42
|
+
|
|
43
|
+
| Framework | Runtime | Average | Ping | Query | Body |
|
|
44
|
+
| ---------------- | ------- | ------- | ---------- | ---------- | ---------- |
|
|
45
|
+
| uws | node | 94,296.49 | 108,551.92 | 104,756.22 | 69,581.33 |
|
|
46
|
+
| bun | bun | 74,824.52 | 85,839.42 | 74,668.88 | 63,965.26 |
|
|
47
|
+
| elysia | bun | 72,112.447 | 82,589.71 | 69,356.08 | 64,391.55 |
|
|
48
|
+
| hyper-express | node | 66,356.707 | 80,002.53 | 69,953.76 | 49,113.83 |
|
|
49
|
+
| hono | bun | 63,944.627 | 74,550.47 | 62,810.28 | 54,473.13 |
|
|
50
|
+
| **ultimate-express** | **node** | **44,081.737** | **51,753.24** | **48,389.84** | **32,102.13** |
|
|
51
|
+
| oak | deno | 40,878.467 | 68,429.24 | 28,541.99 | 25,664.17 |
|
|
52
|
+
| express | bun | 35,937.977 | 41,329.97 | 34,339.79 | 32,144.17 |
|
|
53
|
+
| h3 | node | 35,423.263 | 41,243.68 | 34,429.26 | 30,596.85 |
|
|
54
|
+
| fastify | node | 33,094.62 | 40,147.67 | 40,076.35 | 19,059.84 |
|
|
55
|
+
| oak | bun | 32,705.36 | 35,856.59 | 32,116.4 | 30,143.09 |
|
|
56
|
+
| hono | node | 26,576.02 | 36,215.35 | 34,656.12 | 8,856.59 |
|
|
57
|
+
| acorn | deno | 24,476.67 | 29,690.42 | 22,254.82 | 21,484.77 |
|
|
58
|
+
| koa | node | 24,045.08 | 28,202.12 | 24,590.84 | 19,342.28 |
|
|
59
|
+
| express | node | 10,411.313 | 11,245.57 | 10,598.74 | 9,389.63 |
|
|
60
|
+
|
|
61
|
+
### Performance on real-world application
|
|
62
|
+
|
|
37
63
|
Also tested on a [real-world application](https://nekoweb.org) with templates, static files and dynamic pages with data from database, and showed 1.5-4X speedup in requests per second depending on the page.
|
|
38
64
|
|
|
39
65
|
## Differences from Express
|
|
@@ -110,7 +136,8 @@ In general, basically all features and options are supported. Use [Express 4.x d
|
|
|
110
136
|
|
|
111
137
|
- ✅ express()
|
|
112
138
|
- ✅ express.Router()
|
|
113
|
-
-
|
|
139
|
+
- 🚧 express.json()
|
|
140
|
+
- - ❌ options.inflate
|
|
114
141
|
- ✅ express.urlencoded()
|
|
115
142
|
- ✅ express.static()
|
|
116
143
|
- - Additionally you can pass `options.ifModifiedSince` to support If-Modified-Since header (this header is not supported in normal Express, but is supported in µExpress)
|
package/package.json
CHANGED
package/src/middlewares.js
CHANGED
|
@@ -16,6 +16,7 @@ limitations under the License.
|
|
|
16
16
|
|
|
17
17
|
const fs = require('fs');
|
|
18
18
|
const path = require('path');
|
|
19
|
+
const bytes = require('bytes');
|
|
19
20
|
|
|
20
21
|
function static(root, options) {
|
|
21
22
|
if(!options) options = {};
|
|
@@ -108,6 +109,68 @@ function static(root, options) {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
|
|
112
|
+
function json(options = {}) {
|
|
113
|
+
if(typeof options !== 'object') {
|
|
114
|
+
options = {};
|
|
115
|
+
}
|
|
116
|
+
if(typeof options.limit === 'undefined') options.limit = bytes('100kb');
|
|
117
|
+
else options.limit = bytes(options.limit);
|
|
118
|
+
|
|
119
|
+
if(typeof options.type === 'undefined') options.type = 'application/json';
|
|
120
|
+
else if(typeof options.type !== 'string') {
|
|
121
|
+
throw new Error('type must be a string');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return (req, res, next) => {
|
|
125
|
+
const type = req.headers['content-type'];
|
|
126
|
+
const semiColonIndex = type.indexOf(';');
|
|
127
|
+
const contentType = semiColonIndex !== -1 ? type.substring(0, semiColonIndex) : type;
|
|
128
|
+
if(!type || contentType !== options.type) {
|
|
129
|
+
return next();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// skip reading body for non-POST requests
|
|
133
|
+
// this makes it +10k req/sec faster
|
|
134
|
+
const additionalMethods = req.app.get('body methods');
|
|
135
|
+
if(
|
|
136
|
+
req.method !== 'POST' &&
|
|
137
|
+
req.method !== 'PUT' &&
|
|
138
|
+
req.method !== 'PATCH' &&
|
|
139
|
+
(!additionalMethods || !additionalMethods.includes(req.method))
|
|
140
|
+
) {
|
|
141
|
+
return next();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const abs = [], totalSize = 0;
|
|
145
|
+
req._res.onData((ab, isLast) => {
|
|
146
|
+
abs.push(ab);
|
|
147
|
+
totalSize += ab.length;
|
|
148
|
+
if(totalSize > options.limit) {
|
|
149
|
+
return next(new Error('Request entity too large'));
|
|
150
|
+
}
|
|
151
|
+
if(isLast) {
|
|
152
|
+
const buf = Buffer.concat(abs);
|
|
153
|
+
if(options.verify) {
|
|
154
|
+
try {
|
|
155
|
+
options.verify(req, res, buf);
|
|
156
|
+
} catch(e) {
|
|
157
|
+
return next(e);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
req.body = JSON.parse(buf, options.reviver);
|
|
161
|
+
if(options.strict) {
|
|
162
|
+
if(req.body && typeof req.body !== 'object') {
|
|
163
|
+
return next(new Error('Invalid body'));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
next();
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
}
|
|
173
|
+
|
|
111
174
|
module.exports = {
|
|
112
175
|
static
|
|
113
176
|
};
|