express-toolkit 0.7.1 → 0.8.2
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 +7 -0
- package/package.json +14 -12
- package/src/controller.js +14 -6
- package/src/router.js +12 -10
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ Tiny little utilities for reducing expressjs boilerplate code when building simp
|
|
|
19
19
|
- [Default endpoints](#default-endpoints)
|
|
20
20
|
- [Disable endpoints](#disable-endpoints)
|
|
21
21
|
- [Sorting](#sorting)
|
|
22
|
+
- [Pagination](#pagination)
|
|
22
23
|
- [Projection](#projection)
|
|
23
24
|
- [Custom primary key](#custom-primary-key)
|
|
24
25
|
- [Hooks](#hooks)
|
|
@@ -237,6 +238,12 @@ For example, the following request would sort dinosaurs by age, oldest to younge
|
|
|
237
238
|
```http
|
|
238
239
|
GET /dinosaurs?sortby=age&sortorder=DESC
|
|
239
240
|
```
|
|
241
|
+
## Pagination
|
|
242
|
+
GET endpoints support result pagination through `skip` and `limit` parameters:
|
|
243
|
+
- `skip <Number>` tells the endpoint how many results to skip
|
|
244
|
+
- `limit <Number` tells the endpoint how many results to include in the response
|
|
245
|
+
|
|
246
|
+
To implement a pagination scheme, you can leverage these two parameters in the following way: Suppose you want to return `R` results per page and you want to return page number `P`, you just need to set limit to `R` and skip to `(P-1)*R`
|
|
240
247
|
|
|
241
248
|
## Projection
|
|
242
249
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"files": [
|
|
5
5
|
"index.js",
|
|
6
6
|
"src/"
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"test": "nyc --reporter=text ava test/*.spec.js --verbose",
|
|
17
17
|
"test-ci": "npx nyc --reporter=lcov ava test/*.spec.js --verbose",
|
|
18
18
|
"commit": "npx git-cz",
|
|
19
|
-
"
|
|
19
|
+
"documentation": "docsify init ./docs",
|
|
20
|
+
"pre-commit": "npm run lint && npm run test",
|
|
20
21
|
"changelog": "npx easy-changelog --out=./CHANGELOG.md",
|
|
21
22
|
"release": "sh scripts/release.sh"
|
|
22
23
|
},
|
|
@@ -34,24 +35,25 @@
|
|
|
34
35
|
"express"
|
|
35
36
|
],
|
|
36
37
|
"dependencies": {
|
|
37
|
-
"express": "^4.17.
|
|
38
|
+
"express": "^4.17.3",
|
|
38
39
|
"json8-patch": "^1.0.4",
|
|
39
40
|
"mongoose": "^5.12.3",
|
|
40
41
|
"throwable-http-errors": "^2.1.0"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"ava": "^3.
|
|
44
|
+
"ava": "^4.3.1",
|
|
44
45
|
"body-parser": "^1.19.0",
|
|
45
|
-
"eslint": "^
|
|
46
|
-
"eslint-config-standard": "^
|
|
47
|
-
"eslint-plugin-import": "^2.
|
|
48
|
-
"eslint-plugin-node": "^
|
|
49
|
-
"eslint-plugin-promise": "^
|
|
46
|
+
"eslint": "^7.32.0",
|
|
47
|
+
"eslint-config-standard": "^16.0.3",
|
|
48
|
+
"eslint-plugin-import": "^2.25.4",
|
|
49
|
+
"eslint-plugin-node": "^11.1.0",
|
|
50
|
+
"eslint-plugin-promise": "^5.2.0",
|
|
50
51
|
"eslint-plugin-standard": "^4.0.1",
|
|
51
52
|
"husky": "^1.3.1",
|
|
52
|
-
"mongodb-memory-server": "^
|
|
53
|
-
"nyc": "^
|
|
53
|
+
"mongodb-memory-server": "^8.4.0",
|
|
54
|
+
"nyc": "^15.1.0",
|
|
54
55
|
"sinon": "^7.5.0",
|
|
55
|
-
"supertest": "^4.0.2"
|
|
56
|
+
"supertest": "^4.0.2",
|
|
57
|
+
"uuid": "^8.3.2"
|
|
56
58
|
}
|
|
57
59
|
}
|
package/src/controller.js
CHANGED
|
@@ -20,11 +20,13 @@ class Controller {
|
|
|
20
20
|
* @param {String} [config.id] The attribute to use as primary key for findById, updateById and deleteById. Defaults to _id.
|
|
21
21
|
* @param {Number} [config.defaultSkipValue] The default skip value to be used in find() queries
|
|
22
22
|
* @param {Number} [config.defaultLimitValue] The default skip value to be used in find() queries
|
|
23
|
+
* @param {Number} [config.useUpdateOne] If set to true, updateById method will use mongoose's Model.updateOne() instead of Document.save()
|
|
23
24
|
*/
|
|
24
25
|
constructor (config) {
|
|
25
26
|
this.Model = config.model
|
|
26
27
|
this.id = config.id || '_id'
|
|
27
28
|
this.name = config.name
|
|
29
|
+
this.useUpdateOne = Boolean(config.useUpdateOne) || false
|
|
28
30
|
this.defaultSkipValue = config.defaultSkipValue || DEFAULT_SKIP_VALUE
|
|
29
31
|
this.defaultLimitValue = config.defaultLimitValue || DEFAULT_LIMIT_VALUE
|
|
30
32
|
this.__hooks = {}
|
|
@@ -37,10 +39,10 @@ class Controller {
|
|
|
37
39
|
async find (query) {
|
|
38
40
|
let skip = this.defaultSkipValue
|
|
39
41
|
let limit = this.defaultLimitValue
|
|
40
|
-
if (
|
|
42
|
+
if (Object.prototype.hasOwnProperty.call(query, 'limit')) {
|
|
41
43
|
limit = Number(query.limit)
|
|
42
44
|
}
|
|
43
|
-
if (
|
|
45
|
+
if (Object.prototype.hasOwnProperty.call(query, 'skip')) {
|
|
44
46
|
skip = Number(query.skip)
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -180,7 +182,7 @@ class Controller {
|
|
|
180
182
|
const instances = await this.Model.find(query)
|
|
181
183
|
|
|
182
184
|
for (const instance of instances) {
|
|
183
|
-
for (
|
|
185
|
+
for (const k in update) {
|
|
184
186
|
instance.set(k, update[k])
|
|
185
187
|
}
|
|
186
188
|
|
|
@@ -207,7 +209,7 @@ class Controller {
|
|
|
207
209
|
throw new Errors.NotFound()
|
|
208
210
|
}
|
|
209
211
|
|
|
210
|
-
for (
|
|
212
|
+
for (const k in update) {
|
|
211
213
|
instance.set(k, update[k])
|
|
212
214
|
}
|
|
213
215
|
|
|
@@ -216,7 +218,13 @@ class Controller {
|
|
|
216
218
|
throw new Errors.BadRequest(validationError.message)
|
|
217
219
|
}
|
|
218
220
|
|
|
219
|
-
|
|
221
|
+
if (this.useUpdateOne) {
|
|
222
|
+
await this.Model.updateOne({ [this.id]: id }, update)
|
|
223
|
+
|
|
224
|
+
return instance.toJSON()
|
|
225
|
+
} else {
|
|
226
|
+
return instance.save()
|
|
227
|
+
}
|
|
220
228
|
}
|
|
221
229
|
|
|
222
230
|
/**
|
|
@@ -233,7 +241,7 @@ class Controller {
|
|
|
233
241
|
throw new Errors.NotFound()
|
|
234
242
|
}
|
|
235
243
|
// Primary identifiers cannot be replaced
|
|
236
|
-
replacement
|
|
244
|
+
replacement._id = instance._id
|
|
237
245
|
replacement[this.id] = instance[this.id]
|
|
238
246
|
instance.overwrite(replacement)
|
|
239
247
|
|
package/src/router.js
CHANGED
|
@@ -48,10 +48,12 @@ const defaultEndpoints = {
|
|
|
48
48
|
* @param {Object} config
|
|
49
49
|
* @param {Object} config.controller
|
|
50
50
|
* @param {Object} [config.endpoints] Map<String,Boolean>
|
|
51
|
+
* @param {String} [config.id] The named route parameter to use as id. Defaults to "id"
|
|
51
52
|
* @param {Object} [config.options] Express router options. As described in https://expressjs.com/en/api.html#express.router
|
|
52
53
|
*/
|
|
53
54
|
function buildRouter (config) {
|
|
54
55
|
config.options = config.options || {}
|
|
56
|
+
config.id = config.id || 'id'
|
|
55
57
|
const router = express.Router(config.options)
|
|
56
58
|
if (!(config.controller instanceof Controller)) {
|
|
57
59
|
throw new Error('config.controller must be an instance of Controller')
|
|
@@ -64,7 +66,7 @@ function buildRouter (config) {
|
|
|
64
66
|
const endpointsMap = Object.assign({}, defaultEndpoints, config.endpoints)
|
|
65
67
|
|
|
66
68
|
const findByIdMiddleware = asyncMiddleware(async (req, res, next) => {
|
|
67
|
-
const resource = await config.controller.findById(req.params.id, req.query)
|
|
69
|
+
const resource = await config.controller.findById(req.params[config.id], req.query)
|
|
68
70
|
req.toSend = resource
|
|
69
71
|
return next()
|
|
70
72
|
})
|
|
@@ -76,7 +78,7 @@ function buildRouter (config) {
|
|
|
76
78
|
})
|
|
77
79
|
|
|
78
80
|
const updateByIdMiddleware = asyncMiddleware(async (req, res, next) => {
|
|
79
|
-
const resource = await config.controller.updateById(req.params.id, req.body, req.query)
|
|
81
|
+
const resource = await config.controller.updateById(req.params[config.id], req.body, req.query)
|
|
80
82
|
req.toSend = resource
|
|
81
83
|
return next()
|
|
82
84
|
})
|
|
@@ -88,7 +90,7 @@ function buildRouter (config) {
|
|
|
88
90
|
})
|
|
89
91
|
|
|
90
92
|
const deleteByIdMiddleware = asyncMiddleware(async (req, res, next) => {
|
|
91
|
-
await config.controller.deleteById(req.params.id)
|
|
93
|
+
await config.controller.deleteById(req.params[config.id])
|
|
92
94
|
req.toSend = null
|
|
93
95
|
return next()
|
|
94
96
|
})
|
|
@@ -114,13 +116,13 @@ function buildRouter (config) {
|
|
|
114
116
|
})
|
|
115
117
|
|
|
116
118
|
const patchByIdMiddleware = asyncMiddleware(async (req, res, next) => {
|
|
117
|
-
const resource = await config.controller.patchById(req.params.id, req.body)
|
|
119
|
+
const resource = await config.controller.patchById(req.params[config.id], req.body)
|
|
118
120
|
req.toSend = resource
|
|
119
121
|
return next()
|
|
120
122
|
})
|
|
121
123
|
|
|
122
124
|
const replaceByIdMiddleware = asyncMiddleware(async (req, res, next) => {
|
|
123
|
-
const resource = await config.controller.replaceById(req.params.id, req.body)
|
|
125
|
+
const resource = await config.controller.replaceById(req.params[config.id], req.body)
|
|
124
126
|
req.toSend = resource
|
|
125
127
|
return next()
|
|
126
128
|
})
|
|
@@ -138,7 +140,7 @@ function buildRouter (config) {
|
|
|
138
140
|
},
|
|
139
141
|
findById: {
|
|
140
142
|
method: 'get',
|
|
141
|
-
path:
|
|
143
|
+
path: `/:${config.id}`,
|
|
142
144
|
middleware: findByIdMiddleware
|
|
143
145
|
},
|
|
144
146
|
create: {
|
|
@@ -148,7 +150,7 @@ function buildRouter (config) {
|
|
|
148
150
|
},
|
|
149
151
|
updateById: {
|
|
150
152
|
method: 'put',
|
|
151
|
-
path:
|
|
153
|
+
path: `/:${config.id}`,
|
|
152
154
|
middleware: updateByIdMiddleware
|
|
153
155
|
},
|
|
154
156
|
updateByQuery: {
|
|
@@ -158,7 +160,7 @@ function buildRouter (config) {
|
|
|
158
160
|
},
|
|
159
161
|
deleteById: {
|
|
160
162
|
method: 'delete',
|
|
161
|
-
path:
|
|
163
|
+
path: `/:${config.id}`,
|
|
162
164
|
middleware: deleteByIdMiddleware
|
|
163
165
|
},
|
|
164
166
|
deleteByQuery: {
|
|
@@ -168,12 +170,12 @@ function buildRouter (config) {
|
|
|
168
170
|
},
|
|
169
171
|
patchById: {
|
|
170
172
|
method: 'patch',
|
|
171
|
-
path:
|
|
173
|
+
path: `/:${config.id}`,
|
|
172
174
|
middleware: patchByIdMiddleware
|
|
173
175
|
},
|
|
174
176
|
replaceById: {
|
|
175
177
|
method: 'put',
|
|
176
|
-
path:
|
|
178
|
+
path: `/:${config.id}/replace`,
|
|
177
179
|
middleware: replaceByIdMiddleware
|
|
178
180
|
}
|
|
179
181
|
|