router-http 0.0.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/LICENSE.md +21 -0
- package/README.md +148 -0
- package/package.json +92 -0
- package/src/index.js +106 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright © 2023 Kiko Beats <josefrancisco.verdu@gmail.com> (kikobeats.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# http-router
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
[](https://coveralls.io/github/Kikobeats/http-router)
|
|
5
|
+
[](https://www.npmjs.org/package/http-router)
|
|
6
|
+
|
|
7
|
+
An HTTP router focused in only that, similar to [express@router](https://github.com/pillarjs/router), but:
|
|
8
|
+
|
|
9
|
+
- Focused in just one thing.
|
|
10
|
+
- Maintained and well tested.
|
|
11
|
+
- Most of the API is supported.
|
|
12
|
+
- Smaller and portable (less 50kbs).
|
|
13
|
+
|
|
14
|
+
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
|
+
|
|
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 module is different than polka in some aspects:
|
|
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.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
$ npm install http-router --save
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
First, you should to create a router:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
const createRouter = require('http-router')
|
|
36
|
+
|
|
37
|
+
const router = createRouter((err, req, res) => {
|
|
38
|
+
const hasError = err !== undefined
|
|
39
|
+
res.statusCode = hasError ? 500 : 404
|
|
40
|
+
res.end(hasError ? err.message : 'Not Found')
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The router requires a final handler that will be called if an error occurred or none of the routes match.
|
|
45
|
+
|
|
46
|
+
After that, you can declare any HTTP verb route:
|
|
47
|
+
|
|
48
|
+
### Declaring routes
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
/**
|
|
52
|
+
* Declaring multiple routes based on the HTTP verb.
|
|
53
|
+
*/
|
|
54
|
+
router
|
|
55
|
+
.get('/', (req, res) => {
|
|
56
|
+
res.statusCode = 204
|
|
57
|
+
res.end()
|
|
58
|
+
})
|
|
59
|
+
.post('/ping', (req, res) => res.end('pong'))
|
|
60
|
+
.get('/greetings/:name', (req, res) => {
|
|
61
|
+
const { name } = req.params
|
|
62
|
+
res.end(`Hello, ${name}!`)
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Alternatively, you can call `.all` for associate a route for all the verbs:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
/**
|
|
70
|
+
* Declaring a route to match all the HTTP verbs.
|
|
71
|
+
*/
|
|
72
|
+
router.all('/ping', (req, res) => res.end('pong'))
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Declaring middlewares
|
|
76
|
+
|
|
77
|
+
A middleware can be declared at root level:
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
/**
|
|
81
|
+
* Declaring a middleware that will be always executed.
|
|
82
|
+
*/
|
|
83
|
+
router
|
|
84
|
+
.use('/', (req, res, next) => {
|
|
85
|
+
req.timestamp = Date.now()
|
|
86
|
+
next()
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
or for specific routes:
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
/**
|
|
94
|
+
* Declaring a middleware to execute for a certain route path.
|
|
95
|
+
*/
|
|
96
|
+
router
|
|
97
|
+
.use('/greetings', (req, res, next) => {
|
|
98
|
+
req.greetings = 'Greetings'
|
|
99
|
+
next()
|
|
100
|
+
})
|
|
101
|
+
.get('/greetings/:username', (req, res) => {
|
|
102
|
+
res.end(`${req.greetings}, ${req.params.username}`)
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Prefixing routes
|
|
107
|
+
|
|
108
|
+
In case you need you can prefix all the routes:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
routes.get('/', (req, res) => res.end('Welcome to my API!'))
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Prefix all routes with the API version
|
|
115
|
+
*/
|
|
116
|
+
const router = Router(final)
|
|
117
|
+
router
|
|
118
|
+
.use('/latest', routes)
|
|
119
|
+
.use('/v1', routes)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Using the router
|
|
123
|
+
|
|
124
|
+
After the router has been initialized, start using it as handler in your Node.js server:
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
const server = http.createServer(router)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Benchmark
|
|
131
|
+
|
|
132
|
+
The performance is essentially the same than polka, and that is almost x3 faster than express.
|
|
133
|
+
|
|
134
|
+
See [benchmark](/benchmark) sections.
|
|
135
|
+
|
|
136
|
+
## Related
|
|
137
|
+
|
|
138
|
+
- [send-http](https://github.com/Kikobeats/send-http) – A `res.end` with data type detection.
|
|
139
|
+
- [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.
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
**http-router** © [Kiko Beats](https://kikobeats.com), released under the [MIT](https://github.com/Kikobeats/http-router/blob/master/LICENSE.md) License.<br>
|
|
146
|
+
Authored and maintained by [Kiko Beats](https://kikobeats.com) with help from [contributors](https://github.com/Kikobeats/http-router/contributors).
|
|
147
|
+
|
|
148
|
+
> [kikobeats.com](https://kikobeats.com) · GitHub [Kiko Beats](https://github.com/Kikobeats) · Twitter [@Kikobeats](https://twitter.com/Kikobeats)
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "router-http",
|
|
3
|
+
"description": "Simple HTTP router compatible with Express",
|
|
4
|
+
"homepage": "https://nicedoc.io/Kikobeats/http-router",
|
|
5
|
+
"version": "0.0.0",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"author": {
|
|
8
|
+
"email": "josefrancisco.verdu@gmail.com",
|
|
9
|
+
"name": "Kiko Beats",
|
|
10
|
+
"url": "https://kikobeats.com"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/Kikobeats/http-router.git"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/Kikobeats/http-router/issues"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"app",
|
|
21
|
+
"http",
|
|
22
|
+
"http",
|
|
23
|
+
"middleware",
|
|
24
|
+
"pathname",
|
|
25
|
+
"rest",
|
|
26
|
+
"route",
|
|
27
|
+
"router",
|
|
28
|
+
"routes"
|
|
29
|
+
],
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"trouter": "~3.2.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@commitlint/cli": "latest",
|
|
35
|
+
"@commitlint/config-conventional": "latest",
|
|
36
|
+
"ava": "latest",
|
|
37
|
+
"c8": "latest",
|
|
38
|
+
"conventional-github-releaser": "latest",
|
|
39
|
+
"finepack": "latest",
|
|
40
|
+
"git-authors-cli": "latest",
|
|
41
|
+
"got": "11",
|
|
42
|
+
"nano-staged": "latest",
|
|
43
|
+
"npm-check-updates": "latest",
|
|
44
|
+
"prettier-standard": "latest",
|
|
45
|
+
"simple-git-hooks": "latest",
|
|
46
|
+
"standard": "latest",
|
|
47
|
+
"standard-markdown": "latest",
|
|
48
|
+
"standard-version": "latest"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">= 18"
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"src"
|
|
55
|
+
],
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"commitlint": {
|
|
58
|
+
"extends": [
|
|
59
|
+
"@commitlint/config-conventional"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"nano-staged": {
|
|
63
|
+
"*.js,!*.min.js,": [
|
|
64
|
+
"prettier-standard"
|
|
65
|
+
],
|
|
66
|
+
"*.md": [
|
|
67
|
+
"standard-markdown"
|
|
68
|
+
],
|
|
69
|
+
"package.json": [
|
|
70
|
+
"finepack"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"simple-git-hooks": {
|
|
74
|
+
"commit-msg": "npx commitlint --edit",
|
|
75
|
+
"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
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const Trouter = require('trouter')
|
|
4
|
+
|
|
5
|
+
const requiredFinalHandler = () => {
|
|
6
|
+
throw new TypeError('You should to provide a final handler')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ensure input starts with '/'
|
|
11
|
+
*/
|
|
12
|
+
const lead = route => (route.charCodeAt(0) === 47 ? route : `/${route}`)
|
|
13
|
+
|
|
14
|
+
const value = x => {
|
|
15
|
+
const y = x.indexOf('/', 1)
|
|
16
|
+
return y > 1 ? x.substring(0, y) : x
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const mutate = (str, req) => {
|
|
20
|
+
req.url = req.url.substring(str.length) || '/'
|
|
21
|
+
req.path = req.path.substring(str.length) || '/'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class Router extends Trouter {
|
|
25
|
+
constructor (unhandler) {
|
|
26
|
+
super()
|
|
27
|
+
this.unhandler = unhandler
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Middleware per all routes
|
|
32
|
+
*/
|
|
33
|
+
#middlewares = []
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Middleware for specific routes
|
|
37
|
+
*/
|
|
38
|
+
#middlewaresBy = []
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Middleware declaration, where the base is optional
|
|
42
|
+
* .use(one)
|
|
43
|
+
* .use('/v1', one)
|
|
44
|
+
* .use(one, two)
|
|
45
|
+
* .use('/v2', two)
|
|
46
|
+
*/
|
|
47
|
+
use = (base = '/', ...fns) => {
|
|
48
|
+
if (typeof base === 'function') {
|
|
49
|
+
this.#middlewares = this.#middlewares.concat(base, fns)
|
|
50
|
+
} else if (base === '/') {
|
|
51
|
+
this.#middlewares = this.#middlewares.concat(fns)
|
|
52
|
+
} else {
|
|
53
|
+
base = lead(base)
|
|
54
|
+
fns.forEach(fn => {
|
|
55
|
+
const array = this.#middlewaresBy[base] || []
|
|
56
|
+
array.length > 0 || array.push((r, _, nxt) => (mutate(base, r), nxt()))
|
|
57
|
+
this.#middlewaresBy[base] = array.concat(fn)
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
return this
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
handler = (req, res, pathname) => {
|
|
64
|
+
pathname = pathname || req.url
|
|
65
|
+
let fns = []
|
|
66
|
+
let middlewares = this.#middlewares
|
|
67
|
+
const route = this.find(req.method, pathname)
|
|
68
|
+
const base = value((req.path = pathname))
|
|
69
|
+
if (this.#middlewaresBy[base] !== undefined) {
|
|
70
|
+
middlewares = middlewares.concat(this.#middlewaresBy[base])
|
|
71
|
+
}
|
|
72
|
+
if (route) {
|
|
73
|
+
fns = route.handlers
|
|
74
|
+
req.params = { ...req.params, ...route.params }
|
|
75
|
+
}
|
|
76
|
+
fns.push(this.unhandler)
|
|
77
|
+
// Exit if only a single function
|
|
78
|
+
let i = 0
|
|
79
|
+
let len = middlewares.length
|
|
80
|
+
const num = fns.length
|
|
81
|
+
if (len === i && num === 1) return fns[0](undefined, req, res)
|
|
82
|
+
|
|
83
|
+
// Otherwise loop thru all middlware
|
|
84
|
+
const next = err => (err ? this.unhandler(err, req, res, next) : loop())
|
|
85
|
+
|
|
86
|
+
const loop = () => {
|
|
87
|
+
if (res.writableEnded) return
|
|
88
|
+
if (i >= len) return
|
|
89
|
+
try {
|
|
90
|
+
return middlewares[i++](req, res, next)
|
|
91
|
+
} catch (err) {
|
|
92
|
+
return next(err)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
middlewares = middlewares.concat(fns)
|
|
97
|
+
len += num
|
|
98
|
+
loop() // init
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = (finalhandler = requiredFinalHandler()) => {
|
|
103
|
+
const router = new Router(finalhandler)
|
|
104
|
+
const handler = (req, res) => router.handler(req, res)
|
|
105
|
+
return Object.assign(handler, router)
|
|
106
|
+
}
|