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 CHANGED
File without changes
package/README.md CHANGED
@@ -1,30 +1,23 @@
1
- # http-router
1
+ # router-http
2
2
 
3
- ![Last version](https://img.shields.io/github/tag/Kikobeats/http-router.svg?style=flat-square)
4
- [![Coverage Status](https://img.shields.io/coveralls/Kikobeats/http-router.svg?style=flat-square)](https://coveralls.io/github/Kikobeats/http-router)
5
- [![NPM Status](https://img.shields.io/npm/dm/http-router.svg?style=flat-square)](https://www.npmjs.org/package/http-router)
3
+ ![Last version](https://img.shields.io/github/tag/Kikobeats/router-http.svg?style=flat-square)
4
+ [![Coverage Status](https://img.shields.io/coveralls/Kikobeats/router-http.svg?style=flat-square)](https://coveralls.io/github/Kikobeats/router-http)
5
+ [![NPM Status](https://img.shields.io/npm/dm/router-http.svg?style=flat-square)](https://www.npmjs.org/package/router-http)
6
6
 
7
- An HTTP router focused in only that, similar to [express@router](https://github.com/pillarjs/router), but:
7
+ A middleware style router, similar to [express@router](https://github.com/pillarjs/router), plus:
8
8
 
9
- - Focused in just one thing.
9
+ - Faster (x3 compared with Express).
10
10
  - Maintained and well tested.
11
- - Most of the API is supported.
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 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.
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-router --save
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-router')
28
+ const createRouter = require('router-http')
36
29
 
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')
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 = Router(final)
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
- The performance is essentially the same than polka, and that is almost x3 faster than express.
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
- See [benchmark](/benchmark) sections.
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
- **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).
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-router",
5
- "version": "0.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-router.git"
14
+ "url": "git+https://github.com/Kikobeats/router-http.git"
15
15
  },
16
16
  "bugs": {
17
- "url": "https://github.com/Kikobeats/http-router/issues"
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.0"
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 base is optional
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 = (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)
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
- 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)
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, pathname) => {
64
- pathname = pathname || req.url
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 base = value((req.path = pathname))
69
- if (this.#middlewaresBy[base] !== undefined) {
70
- middlewares = middlewares.concat(this.#middlewaresBy[base])
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 i = 0
79
- let len = middlewares.length
92
+ let index = 0
93
+ let size = middlewares.length
80
94
  const num = fns.length
81
- if (len === i && num === 1) return fns[0](undefined, req, res)
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
- 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
- }
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
- len += num
115
+ size += num
98
116
  loop() // init
99
117
  }
100
118
  }