router-http 0.0.0 → 0.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/LICENSE.md +0 -0
- package/README.md +60 -30
- package/package.json +22 -21
- package/src/index.js +50 -32
package/LICENSE.md
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -1,30 +1,23 @@
|
|
|
1
|
-
# http
|
|
1
|
+
# router-http
|
|
2
2
|
|
|
3
|
-

|
|
4
|
+
[](https://coveralls.io/github/Kikobeats/router-http)
|
|
5
|
+
[](https://www.npmjs.org/package/router-http)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A middleware style router, similar to [express@router](https://github.com/pillarjs/router), plus:
|
|
8
8
|
|
|
9
|
-
-
|
|
9
|
+
- Faster (x3 compared with Express).
|
|
10
10
|
- Maintained and well tested.
|
|
11
|
-
-
|
|
12
|
-
- Smaller and portable (less 50kbs).
|
|
11
|
+
- Smaller (1.4 kB).
|
|
13
12
|
|
|
14
13
|
Don't get me wrong: The original Express router is a piece of art. I used it for years and I just considered create this library after experienced a bug that never was addressed in the stable version due to the [lack of maintenance](https://github.com/pillarjs/router/pull/60).
|
|
15
14
|
|
|
16
|
-
While I was evaluating the market for finding an alternative I found [polka](https://github.com/lukeed/polka/tree/master/packages/polka) was a good starting point for creating a replacement. This
|
|
17
|
-
|
|
18
|
-
- This module doesn't take care about the http.Server.
|
|
19
|
-
- This module doesn't use any of the Node.js built-in module, so it can be used in Vercel Edge Functions, Deno or CF Workers.
|
|
20
|
-
- This module doesn't adds `req.query` nor `req.search` (check [to-query](https://github.com/Kikobeats/to-query) for that).
|
|
21
|
-
|
|
22
|
-
In resume: this module does nothing beyond finding the correct path and matching the associated code.
|
|
15
|
+
While I was evaluating the market for finding an alternative, I found [polka](https://github.com/lukeed/polka/tree/master/packages/polka) was a good starting point for creating a replacement. This library is different from polka in that it only contains the code that is strictly necessary for routing, nothing else.
|
|
23
16
|
|
|
24
17
|
## Install
|
|
25
18
|
|
|
26
19
|
```bash
|
|
27
|
-
$ npm install http
|
|
20
|
+
$ npm install router-http --save
|
|
28
21
|
```
|
|
29
22
|
|
|
30
23
|
## Usage
|
|
@@ -32,21 +25,21 @@ $ npm install http-router --save
|
|
|
32
25
|
First, you should to create a router:
|
|
33
26
|
|
|
34
27
|
```js
|
|
35
|
-
const createRouter = require('http
|
|
28
|
+
const createRouter = require('router-http')
|
|
36
29
|
|
|
37
|
-
const router = createRouter((
|
|
38
|
-
const hasError =
|
|
39
|
-
res.statusCode = hasError ? 500 : 404
|
|
40
|
-
res.end(hasError ?
|
|
30
|
+
const router = createRouter((error, req, res) => {
|
|
31
|
+
const hasError = error !== undefined
|
|
32
|
+
res.statusCode = hasError ? error.statusCode ?? 500 : 404
|
|
33
|
+
res.end(hasError ? error.message ?? 'Internal Server Error' : 'Not Found')
|
|
41
34
|
})
|
|
42
35
|
```
|
|
43
36
|
|
|
44
37
|
The router requires a final handler that will be called if an error occurred or none of the routes match.
|
|
45
38
|
|
|
46
|
-
After that, you can declare any HTTP verb route:
|
|
47
|
-
|
|
48
39
|
### Declaring routes
|
|
49
40
|
|
|
41
|
+
The routes are declared using HTTP verbs:
|
|
42
|
+
|
|
50
43
|
```js
|
|
51
44
|
/**
|
|
52
45
|
* Declaring multiple routes based on the HTTP verb.
|
|
@@ -103,6 +96,18 @@ router
|
|
|
103
96
|
})
|
|
104
97
|
```
|
|
105
98
|
|
|
99
|
+
Also, you can declare conditional middlewares:
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
/**
|
|
103
|
+
* Just add the middleware if it's production environment
|
|
104
|
+
*/
|
|
105
|
+
router
|
|
106
|
+
.use(process.env.NODE_ENV === 'production' && authentication())
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
They are only will add if the condition is satisfied.
|
|
110
|
+
|
|
106
111
|
### Prefixing routes
|
|
107
112
|
|
|
108
113
|
In case you need you can prefix all the routes:
|
|
@@ -113,7 +118,7 @@ routes.get('/', (req, res) => res.end('Welcome to my API!'))
|
|
|
113
118
|
/**
|
|
114
119
|
* Prefix all routes with the API version
|
|
115
120
|
*/
|
|
116
|
-
const router =
|
|
121
|
+
const router = createRouter(final)
|
|
117
122
|
router
|
|
118
123
|
.use('/latest', routes)
|
|
119
124
|
.use('/v1', routes)
|
|
@@ -129,20 +134,45 @@ const server = http.createServer(router)
|
|
|
129
134
|
|
|
130
135
|
## Benchmark
|
|
131
136
|
|
|
132
|
-
|
|
137
|
+
**express@4.18.2**
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
Running 30s test @ http://localhost:3000/user/123
|
|
141
|
+
8 threads and 100 connections
|
|
142
|
+
Thread Stats Avg Stdev Max +/- Stdev
|
|
143
|
+
Latency 4.12ms 653.26us 21.71ms 89.35%
|
|
144
|
+
Req/Sec 2.93k 159.60 5.99k 84.75%
|
|
145
|
+
700421 requests in 30.06s, 102.87MB read
|
|
146
|
+
Requests/sec: 23304.22
|
|
147
|
+
Transfer/sec: 3.42MB
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**router-http@1.0.0**
|
|
133
151
|
|
|
134
|
-
|
|
152
|
+
```
|
|
153
|
+
Running 30s test @ http://localhost:3000/user/123
|
|
154
|
+
8 threads and 100 connections
|
|
155
|
+
Thread Stats Avg Stdev Max +/- Stdev
|
|
156
|
+
Latency 1.33ms 690.36us 30.28ms 97.16%
|
|
157
|
+
Req/Sec 9.27k 1.09k 11.76k 89.58%
|
|
158
|
+
2214097 requests in 30.02s, 276.61MB read
|
|
159
|
+
Requests/sec: 73754.53
|
|
160
|
+
Transfer/sec: 9.21MB
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
See more details, check [benchmark](/benchmark) section.
|
|
135
164
|
|
|
136
165
|
## Related
|
|
137
166
|
|
|
138
167
|
- [send-http](https://github.com/Kikobeats/send-http) – A `res.end` with data type detection.
|
|
139
168
|
- [http-body](https://github.com/Kikobeats/http-body) – Parse the http.IncomingMessage body into text/json/buffer.
|
|
140
|
-
- [http-compression](https://github.com/Kikobeats/http-compression) – Adding compression (gzip/brotli) for your HTTP server in Node.js
|
|
141
|
-
- [to-query](https://github.com/Kikobeats/to-query) Get query object from a request url.
|
|
169
|
+
- [http-compression](https://github.com/Kikobeats/http-compression) – Adding compression (gzip/brotli) for your HTTP server in Node.js.
|
|
142
170
|
|
|
143
171
|
## License
|
|
144
172
|
|
|
145
|
-
|
|
146
|
-
|
|
173
|
+
Full credits to [Luke Edwards](https://github.com/lukeed) for writing [Polka](https://github.com/lukeed/polka) and inspired this project.
|
|
174
|
+
|
|
175
|
+
**router-http** © [Kiko Beats](https://kikobeats.com), released under the [MIT](https://github.com/Kikobeats/router-http/blob/master/LICENSE.md) License.<br>
|
|
176
|
+
Authored and maintained by [Kiko Beats](https://kikobeats.com) with help from [contributors](https://github.com/Kikobeats/router-http/contributors).
|
|
147
177
|
|
|
148
178
|
> [kikobeats.com](https://kikobeats.com) · GitHub [Kiko Beats](https://github.com/Kikobeats) · Twitter [@Kikobeats](https://twitter.com/Kikobeats)
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "router-http",
|
|
3
3
|
"description": "Simple HTTP router compatible with Express",
|
|
4
|
-
"homepage": "https://nicedoc.io/Kikobeats/http
|
|
5
|
-
"version": "0.0.
|
|
4
|
+
"homepage": "https://nicedoc.io/Kikobeats/router-http",
|
|
5
|
+
"version": "0.0.1",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"author": {
|
|
8
8
|
"email": "josefrancisco.verdu@gmail.com",
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
},
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
|
-
"url": "git+https://github.com/Kikobeats/http
|
|
14
|
+
"url": "git+https://github.com/Kikobeats/router-http.git"
|
|
15
15
|
},
|
|
16
16
|
"bugs": {
|
|
17
|
-
"url": "https://github.com/Kikobeats/http
|
|
17
|
+
"url": "https://github.com/Kikobeats/router-http/issues"
|
|
18
18
|
},
|
|
19
19
|
"keywords": [
|
|
20
20
|
"app",
|
|
@@ -28,13 +28,14 @@
|
|
|
28
28
|
"routes"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"trouter": "~3.2.
|
|
31
|
+
"trouter": "~3.2.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@commitlint/cli": "latest",
|
|
35
35
|
"@commitlint/config-conventional": "latest",
|
|
36
36
|
"ava": "latest",
|
|
37
37
|
"c8": "latest",
|
|
38
|
+
"ci-publish": "latest",
|
|
38
39
|
"conventional-github-releaser": "latest",
|
|
39
40
|
"finepack": "latest",
|
|
40
41
|
"git-authors-cli": "latest",
|
|
@@ -53,6 +54,21 @@
|
|
|
53
54
|
"files": [
|
|
54
55
|
"src"
|
|
55
56
|
],
|
|
57
|
+
"scripts": {
|
|
58
|
+
"clean": "rm -rf node_modules",
|
|
59
|
+
"contributors": "(npx git-authors-cli && npx finepack && git add package.json && git commit -m 'build: contributors' --no-verify) || true",
|
|
60
|
+
"coverage": "c8 report --reporter=text-lcov > coverage/lcov.info",
|
|
61
|
+
"lint": "standard-markdown README.md && standard",
|
|
62
|
+
"postrelease": "npm run release:tags && npm run release:github && (ci-publish || npm publish --access=public)",
|
|
63
|
+
"prerelease": "npm run update:check && npm run contributors",
|
|
64
|
+
"pretest": "npm run lint",
|
|
65
|
+
"release": "standard-version -a",
|
|
66
|
+
"release:github": "conventional-github-releaser -p angular",
|
|
67
|
+
"release:tags": "git push --follow-tags origin HEAD:master",
|
|
68
|
+
"test": "c8 ava",
|
|
69
|
+
"update": "ncu -u",
|
|
70
|
+
"update:check": "ncu -- --error-level 2"
|
|
71
|
+
},
|
|
56
72
|
"license": "MIT",
|
|
57
73
|
"commitlint": {
|
|
58
74
|
"extends": [
|
|
@@ -73,20 +89,5 @@
|
|
|
73
89
|
"simple-git-hooks": {
|
|
74
90
|
"commit-msg": "npx commitlint --edit",
|
|
75
91
|
"pre-commit": "npx nano-staged"
|
|
76
|
-
},
|
|
77
|
-
"scripts": {
|
|
78
|
-
"clean": "rm -rf node_modules",
|
|
79
|
-
"contributors": "(npx git-authors-cli && npx finepack && git add package.json && git commit -m 'build: contributors' --no-verify) || true",
|
|
80
|
-
"coverage": "c8 report --reporter=text-lcov > coverage/lcov.info",
|
|
81
|
-
"lint": "standard-markdown README.md && standard",
|
|
82
|
-
"postrelease": "npm run release:tags && npm run release:github && npm publish",
|
|
83
|
-
"prerelease": "npm run update:check",
|
|
84
|
-
"pretest": "npm run lint",
|
|
85
|
-
"release": "standard-version -a",
|
|
86
|
-
"release:github": "conventional-github-releaser -p angular",
|
|
87
|
-
"release:tags": "git push --follow-tags origin HEAD:master",
|
|
88
|
-
"test": "c8 ava",
|
|
89
|
-
"update": "ncu -u",
|
|
90
|
-
"update:check": "ncu -- --error-level 2"
|
|
91
92
|
}
|
|
92
|
-
}
|
|
93
|
+
}
|
package/src/index.js
CHANGED
|
@@ -16,9 +16,20 @@ const value = x => {
|
|
|
16
16
|
return y > 1 ? x.substring(0, y) : x
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
const parse = ({ url }) => {
|
|
20
|
+
const index = url.indexOf('?', 1)
|
|
21
|
+
const obj = { pathname: url, query: null, search: null }
|
|
22
|
+
if (index !== -1) {
|
|
23
|
+
obj.search = url.substring(index)
|
|
24
|
+
obj.query = obj.search.substring(1)
|
|
25
|
+
obj.pathname = url.substring(0, index)
|
|
26
|
+
}
|
|
27
|
+
return obj
|
|
28
|
+
}
|
|
29
|
+
|
|
19
30
|
const mutate = (str, req) => {
|
|
20
|
-
req.url = req.url.substring(str.length)
|
|
21
|
-
req.path = req.path.substring(str.length)
|
|
31
|
+
req.url = req.url.substring(str.length) ?? '/'
|
|
32
|
+
req.path = req.path.substring(str.length) ?? '/'
|
|
22
33
|
}
|
|
23
34
|
|
|
24
35
|
class Router extends Trouter {
|
|
@@ -38,63 +49,70 @@ class Router extends Trouter {
|
|
|
38
49
|
#middlewaresBy = []
|
|
39
50
|
|
|
40
51
|
/**
|
|
41
|
-
* Middleware declaration, where the
|
|
52
|
+
* Middleware declaration, where the page is optional
|
|
42
53
|
* .use(one)
|
|
43
54
|
* .use('/v1', one)
|
|
44
55
|
* .use(one, two)
|
|
45
56
|
* .use('/v2', two)
|
|
46
57
|
*/
|
|
47
|
-
use = (
|
|
48
|
-
if (typeof
|
|
49
|
-
this.#middlewares = this.#middlewares.concat(
|
|
50
|
-
} else if (
|
|
51
|
-
this.#middlewares = this.#middlewares.concat(fns)
|
|
58
|
+
use = (page = '/', ...fns) => {
|
|
59
|
+
if (typeof page === 'function' || typeof page === 'boolean') {
|
|
60
|
+
this.#middlewares = this.#middlewares.concat(page, fns).filter(Boolean)
|
|
61
|
+
} else if (page === '/') {
|
|
62
|
+
this.#middlewares = this.#middlewares.concat(fns).filter(Boolean)
|
|
52
63
|
} else {
|
|
53
|
-
|
|
54
|
-
fns.forEach(fn => {
|
|
55
|
-
const array = this.#middlewaresBy[
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
page = lead(page)
|
|
65
|
+
fns.filter(Boolean).forEach(fn => {
|
|
66
|
+
const array = this.#middlewaresBy[page] ?? []
|
|
67
|
+
// eslint-disable-next-line no-sequences
|
|
68
|
+
array.length > 0 || array.push((r, _, nxt) => (mutate(page, r), nxt()))
|
|
69
|
+
this.#middlewaresBy[page] = array.concat(fn)
|
|
58
70
|
})
|
|
59
71
|
}
|
|
60
72
|
return this
|
|
61
73
|
}
|
|
62
74
|
|
|
63
|
-
handler = (req, res,
|
|
64
|
-
|
|
75
|
+
handler = (req, res, info) => {
|
|
76
|
+
info = info ?? parse(req)
|
|
65
77
|
let fns = []
|
|
66
78
|
let middlewares = this.#middlewares
|
|
67
|
-
const route = this.find(req.method, pathname)
|
|
68
|
-
const
|
|
69
|
-
if (this.#middlewaresBy[
|
|
70
|
-
middlewares = middlewares.concat(this.#middlewaresBy[
|
|
79
|
+
const route = this.find(req.method, info.pathname)
|
|
80
|
+
const page = value((req.path = info.pathname))
|
|
81
|
+
if (this.#middlewaresBy[page] !== undefined) {
|
|
82
|
+
middlewares = middlewares.concat(this.#middlewaresBy[page])
|
|
71
83
|
}
|
|
72
84
|
if (route) {
|
|
73
85
|
fns = route.handlers
|
|
74
86
|
req.params = { ...req.params, ...route.params }
|
|
75
87
|
}
|
|
76
88
|
fns.push(this.unhandler)
|
|
89
|
+
req.search = req.query ?? info.search
|
|
90
|
+
req.query = req.query ?? info.query
|
|
77
91
|
// Exit if only a single function
|
|
78
|
-
let
|
|
79
|
-
let
|
|
92
|
+
let index = 0
|
|
93
|
+
let size = middlewares.length
|
|
80
94
|
const num = fns.length
|
|
81
|
-
if (
|
|
95
|
+
if (size === index && num === 1) return fns[0](undefined, req, res)
|
|
82
96
|
|
|
83
97
|
// Otherwise loop thru all middlware
|
|
84
98
|
const next = err => (err ? this.unhandler(err, req, res, next) : loop())
|
|
85
99
|
|
|
86
|
-
const loop = () =>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
const loop = () =>
|
|
101
|
+
res.writableEnded ||
|
|
102
|
+
(index < size &&
|
|
103
|
+
(() => {
|
|
104
|
+
try {
|
|
105
|
+
const mware = middlewares[index++]
|
|
106
|
+
return index === size
|
|
107
|
+
? mware(undefined, req, res, next)
|
|
108
|
+
: mware(req, res, next)
|
|
109
|
+
} catch (err) {
|
|
110
|
+
return this.unhandler(err, req, res, next)
|
|
111
|
+
}
|
|
112
|
+
})())
|
|
95
113
|
|
|
96
114
|
middlewares = middlewares.concat(fns)
|
|
97
|
-
|
|
115
|
+
size += num
|
|
98
116
|
loop() // init
|
|
99
117
|
}
|
|
100
118
|
}
|