json-server 0.17.2 → 0.17.4

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 CHANGED
@@ -35,22 +35,6 @@ See also:
35
35
 
36
36
  <p>&nbsp;</p>
37
37
 
38
- <p align="center">
39
- <a href="https://megafamous.com/buy-instagram-followers" target="_blank">
40
- <img src="https://jsonplaceholder.typicode.com/megafamous.png" height="70px">
41
- </a>
42
- </p>
43
-
44
- <p>&nbsp;</p>
45
-
46
- <p align="center">
47
- <a href="http://asocks.com/c/TFw5bz" target="_blank">
48
- <img src="https://jsonplaceholder.typicode.com/asocks.png" height="70px">
49
- </a>
50
- </p>
51
-
52
- <p>&nbsp;</p>
53
-
54
38
  <p>&nbsp;</p>
55
39
 
56
40
  <h2 align="center">Silver sponsors 🥈</h2>
@@ -371,7 +355,7 @@ Create a `routes.json` file. Pay attention to start every route with `/`.
371
355
  "/api/*": "/$1",
372
356
  "/:resource/:id/show": "/:resource/:id",
373
357
  "/posts/:category": "/posts?category=:category",
374
- "/articles\\?id=:id": "/posts/:id"
358
+ "/articles?id=:id": "/posts/:id"
375
359
  }
376
360
  ```
377
361
 
package/lib/cli/bin.js CHANGED
@@ -2,5 +2,4 @@
2
2
  "use strict";
3
3
 
4
4
  require('please-upgrade-node')(require('../../package.json'));
5
-
6
5
  require('./')();
package/lib/cli/index.js CHANGED
@@ -1,11 +1,8 @@
1
1
  "use strict";
2
2
 
3
3
  const yargs = require('yargs');
4
-
5
4
  const run = require('./run');
6
-
7
5
  const pkg = require('../../package.json');
8
-
9
6
  module.exports = function () {
10
7
  const argv = yargs.config('config').usage('$0 [options] <source>').options({
11
8
  port: {
@@ -76,6 +73,5 @@ module.exports = function () {
76
73
  default: 'json-server.json'
77
74
  }
78
75
  }).boolean('watch').boolean('read-only').boolean('quiet').boolean('no-cors').boolean('no-gzip').help('help').alias('help', 'h').version(pkg.version).alias('version', 'v').example('$0 db.json', '').example('$0 file.js', '').example('$0 http://example.com/db.json', '').epilog('https://github.com/typicode/json-server').require(1, 'Missing <source> argument').argv;
79
-
80
76
  run(argv);
81
77
  };
package/lib/cli/run.js CHANGED
@@ -1,49 +1,36 @@
1
1
  "use strict";
2
2
 
3
3
  const fs = require('fs');
4
-
5
4
  const path = require('path');
6
-
7
5
  const jph = require('json-parse-helpfulerror');
8
-
9
6
  const _ = require('lodash');
10
-
11
7
  const chalk = require('chalk');
12
-
13
8
  const enableDestroy = require('server-destroy');
14
-
15
9
  const pause = require('connect-pause');
16
-
17
10
  const is = require('./utils/is');
18
-
19
11
  const load = require('./utils/load');
20
-
21
12
  const jsonServer = require('../server');
22
-
23
13
  function prettyPrint(argv, object, rules) {
24
14
  const root = `http://${argv.host}:${argv.port}`;
25
15
  console.log();
26
16
  console.log(chalk.bold(' Resources'));
27
-
28
17
  for (const prop in object) {
18
+ // skip printing $schema nodes
19
+ if (prop === '$schema') continue;
29
20
  console.log(` ${root}/${prop}`);
30
21
  }
31
-
32
22
  if (rules) {
33
23
  console.log();
34
24
  console.log(chalk.bold(' Other routes'));
35
-
36
25
  for (const rule in rules) {
37
26
  console.log(` ${rule} -> ${rules[rule]}`);
38
27
  }
39
28
  }
40
-
41
29
  console.log();
42
30
  console.log(chalk.bold(' Home'));
43
31
  console.log(` ${root}`);
44
32
  console.log();
45
33
  }
46
-
47
34
  function createApp(db, routes, middlewares, argv) {
48
35
  const app = jsonServer.create();
49
36
  const {
@@ -59,98 +46,92 @@ function createApp(db, routes, middlewares, argv) {
59
46
  noGzip: argv.noGzip,
60
47
  bodyParser: true
61
48
  };
62
-
63
49
  if (argv.static) {
64
50
  defaultsOpts.static = path.join(process.cwd(), argv.static);
65
51
  }
66
-
67
52
  const defaults = jsonServer.defaults(defaultsOpts);
68
53
  app.use(defaults);
69
-
70
54
  if (routes) {
71
55
  const rewriter = jsonServer.rewriter(routes);
72
56
  app.use(rewriter);
73
57
  }
74
-
75
58
  if (middlewares) {
76
59
  app.use(middlewares);
77
60
  }
78
-
79
61
  if (argv.delay) {
80
62
  app.use(pause(argv.delay));
81
63
  }
82
-
83
64
  router.db._.id = argv.id;
84
65
  app.db = router.db;
85
66
  app.use(router);
86
67
  return app;
87
68
  }
88
-
89
69
  module.exports = function (argv) {
90
70
  const source = argv._[0];
91
71
  let app;
92
72
  let server;
93
-
94
73
  if (!fs.existsSync(argv.snapshots)) {
95
74
  console.log(`Error: snapshots directory ${argv.snapshots} doesn't exist`);
96
75
  process.exit(1);
97
- } // noop log fn
98
-
76
+ }
99
77
 
78
+ // noop log fn
100
79
  if (argv.quiet) {
101
80
  console.log = () => {};
102
81
  }
103
-
104
82
  console.log();
105
83
  console.log(chalk.cyan(' \\{^_^}/ hi!'));
106
-
107
84
  function start(cb) {
108
85
  console.log();
109
86
  console.log(chalk.gray(' Loading', source));
110
- server = undefined; // create db and load object, JSON file, JS or HTTP database
87
+ server = undefined;
111
88
 
89
+ // create db and load object, JSON file, JS or HTTP database
112
90
  return load(source).then(db => {
113
91
  // Load additional routes
114
92
  let routes;
115
-
116
93
  if (argv.routes) {
117
94
  console.log(chalk.gray(' Loading', argv.routes));
118
95
  routes = JSON.parse(fs.readFileSync(argv.routes));
119
- } // Load middlewares
120
-
96
+ }
121
97
 
98
+ // Load middlewares
122
99
  let middlewares;
123
-
124
100
  if (argv.middlewares) {
125
101
  middlewares = argv.middlewares.map(function (m) {
126
102
  console.log(chalk.gray(' Loading', m));
127
103
  return require(path.resolve(m));
128
104
  });
129
- } // Done
130
-
105
+ }
131
106
 
132
- console.log(chalk.gray(' Done')); // Create app and server
107
+ // Done
108
+ console.log(chalk.gray(' Done'));
133
109
 
110
+ // Create app and server
134
111
  app = createApp(db, routes, middlewares, argv);
135
- server = app.listen(argv.port, argv.host); // Enhance with a destroy function
112
+ server = app.listen(argv.port, argv.host);
136
113
 
137
- enableDestroy(server); // Display server informations
114
+ // Enhance with a destroy function
115
+ enableDestroy(server);
138
116
 
139
- prettyPrint(argv, db.getState(), routes); // Catch and handle any error occurring in the server process
117
+ // Display server informations
118
+ prettyPrint(argv, db.getState(), routes);
140
119
 
120
+ // Catch and handle any error occurring in the server process
141
121
  process.on('uncaughtException', error => {
142
122
  if (error.errno === 'EADDRINUSE') console.log(chalk.red(`Cannot bind to the port ${error.port}. Please specify another port number either through --port argument or through the json-server.json configuration file`));else console.log('Some error occurred', error);
143
123
  process.exit(1);
144
124
  });
145
125
  });
146
- } // Start server
147
-
126
+ }
148
127
 
128
+ // Start server
149
129
  start().then(() => {
150
130
  // Snapshot
151
- console.log(chalk.gray(' Type s + enter at any time to create a snapshot of the database')); // Support nohup
152
- // https://github.com/typicode/json-server/issues/221
131
+ console.log(chalk.gray(' Type s + enter at any time to create a snapshot of the database'));
153
132
 
133
+ // Support nohup
134
+ // https://github.com/typicode/json-server/issues/221
154
135
  process.stdin.on('error', () => {
155
136
  console.log(` Error, can't read from stdin`);
156
137
  console.log(` Creating a snapshot from the CLI won't be possible`);
@@ -164,16 +145,19 @@ module.exports = function (argv) {
164
145
  fs.writeFileSync(file, JSON.stringify(state, null, 2), 'utf-8');
165
146
  console.log(` Saved snapshot to ${path.relative(process.cwd(), file)}\n`);
166
147
  }
167
- }); // Watch files
148
+ });
168
149
 
150
+ // Watch files
169
151
  if (argv.watch) {
170
152
  console.log(chalk.gray(' Watching...'));
171
153
  console.log();
172
- const source = argv._[0]; // Can't watch URL
154
+ const source = argv._[0];
173
155
 
174
- if (is.URL(source)) throw new Error("Can't watch URL"); // Watch .js or .json file
175
- // Since lowdb uses atomic writing, directory is watched instead of file
156
+ // Can't watch URL
157
+ if (is.URL(source)) throw new Error("Can't watch URL");
176
158
 
159
+ // Watch .js or .json file
160
+ // Since lowdb uses atomic writing, directory is watched instead of file
177
161
  const watchedDir = path.dirname(source);
178
162
  let readError = false;
179
163
  fs.watch(watchedDir, (event, file) => {
@@ -181,14 +165,11 @@ module.exports = function (argv) {
181
165
  // file can be null
182
166
  if (file) {
183
167
  const watchedFile = path.resolve(watchedDir, file);
184
-
185
168
  if (watchedFile === path.resolve(source)) {
186
169
  if (is.FILE(watchedFile)) {
187
170
  let obj;
188
-
189
171
  try {
190
172
  obj = jph.parse(fs.readFileSync(watchedFile));
191
-
192
173
  if (readError) {
193
174
  console.log(chalk.green(` Read error has been fixed :)`));
194
175
  readError = false;
@@ -198,11 +179,10 @@ module.exports = function (argv) {
198
179
  console.log(chalk.red(` Error reading ${watchedFile}`));
199
180
  console.error(e.message);
200
181
  return;
201
- } // Compare .json file content with in memory database
202
-
182
+ }
203
183
 
184
+ // Compare .json file content with in memory database
204
185
  const isDatabaseDifferent = !_.isEqual(obj, app.db.getState());
205
-
206
186
  if (isDatabaseDifferent) {
207
187
  console.log(chalk.gray(` ${source} has changed, reloading...`));
208
188
  server && server.destroy(() => start());
@@ -210,14 +190,14 @@ module.exports = function (argv) {
210
190
  }
211
191
  }
212
192
  }
213
- }); // Watch routes
193
+ });
214
194
 
195
+ // Watch routes
215
196
  if (argv.routes) {
216
197
  const watchedDir = path.dirname(argv.routes);
217
198
  fs.watch(watchedDir, (event, file) => {
218
199
  if (file) {
219
200
  const watchedFile = path.resolve(watchedDir, file);
220
-
221
201
  if (watchedFile === path.resolve(argv.routes)) {
222
202
  console.log(chalk.gray(` ${argv.routes} has changed, reloading...`));
223
203
  server && server.destroy(() => start());
@@ -5,15 +5,12 @@ module.exports = {
5
5
  JS,
6
6
  URL
7
7
  };
8
-
9
8
  function FILE(s) {
10
9
  return !URL(s) && /\.json$/.test(s);
11
10
  }
12
-
13
11
  function JS(s) {
14
12
  return !URL(s) && /\.c?js$/.test(s);
15
13
  }
16
-
17
14
  function URL(s) {
18
15
  return /^(http|https):/.test(s);
19
16
  }
@@ -1,23 +1,14 @@
1
1
  "use strict";
2
2
 
3
3
  const fs = require('fs');
4
-
5
4
  const path = require('path');
6
-
7
5
  const http = require('http');
8
-
9
6
  const https = require('https');
10
-
11
7
  const low = require('lowdb');
12
-
13
8
  const FileAsync = require('lowdb/adapters/FileAsync');
14
-
15
9
  const Memory = require('lowdb/adapters/Memory');
16
-
17
10
  const is = require('./is');
18
-
19
11
  const chalk = require('chalk');
20
-
21
12
  const example = {
22
13
  posts: [{
23
14
  id: 1,
@@ -33,7 +24,6 @@ const example = {
33
24
  name: 'typicode'
34
25
  }
35
26
  };
36
-
37
27
  module.exports = function (source) {
38
28
  return new Promise((resolve, reject) => {
39
29
  if (is.FILE(source)) {
@@ -43,12 +33,11 @@ module.exports = function (source) {
43
33
  console.log();
44
34
  fs.writeFileSync(source, JSON.stringify(example, null, 2));
45
35
  }
46
-
47
36
  resolve(low(new FileAsync(source)));
48
37
  } else if (is.URL(source)) {
49
38
  // Normalize the source into a URL object.
50
- const sourceUrl = new URL(source); // Pick the client based on the protocol scheme
51
-
39
+ const sourceUrl = new URL(source);
40
+ // Pick the client based on the protocol scheme
52
41
  const client = sourceUrl.protocol === 'https:' ? https : http;
53
42
  client.get(sourceUrl, res => {
54
43
  let dbData = '';
@@ -65,14 +54,12 @@ module.exports = function (source) {
65
54
  // Clear cache
66
55
  const filename = path.resolve(source);
67
56
  delete require.cache[filename];
68
-
69
57
  const dataFn = require(filename);
70
-
71
58
  if (typeof dataFn !== 'function') {
72
59
  throw new Error('The database is a JavaScript file but the export is not a function.');
73
- } // Run dataFn to generate data
74
-
60
+ }
75
61
 
62
+ // Run dataFn to generate data
76
63
  const data = dataFn();
77
64
  resolve(low(new Memory()).setState(data));
78
65
  } else {
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
 
3
3
  const bodyParser = require('body-parser');
4
-
5
4
  module.exports = [bodyParser.json({
6
5
  limit: '10mb',
7
6
  extended: false
@@ -1,21 +1,13 @@
1
1
  "use strict";
2
2
 
3
3
  const fs = require('fs');
4
-
5
4
  const path = require('path');
6
-
7
5
  const express = require('express');
8
-
9
6
  const logger = require('morgan');
10
-
11
7
  const cors = require('cors');
12
-
13
8
  const compression = require('compression');
14
-
15
9
  const errorhandler = require('errorhandler');
16
-
17
10
  const bodyParser = require('./body-parser');
18
-
19
11
  module.exports = function (opts) {
20
12
  const userDir = path.join(process.cwd(), 'public');
21
13
  const defaultDir = path.join(__dirname, '../../public');
@@ -24,43 +16,45 @@ module.exports = function (opts) {
24
16
  logger: true,
25
17
  static: staticDir
26
18
  }, opts);
27
- const arr = []; // Compress all requests
19
+ const arr = [];
28
20
 
21
+ // Compress all requests
29
22
  if (!opts.noGzip) {
30
23
  arr.push(compression());
31
- } // Enable CORS for all the requests, including static files
32
-
24
+ }
33
25
 
26
+ // Enable CORS for all the requests, including static files
34
27
  if (!opts.noCors) {
35
28
  arr.push(cors({
36
29
  origin: true,
37
30
  credentials: true
38
31
  }));
39
32
  }
40
-
41
33
  if (process.env.NODE_ENV === 'development') {
42
34
  // only use in development
43
35
  arr.push(errorhandler());
44
- } // Serve static files
45
-
36
+ }
46
37
 
47
- arr.push(express.static(opts.static)); // Logger
38
+ // Serve static files
39
+ arr.push(express.static(opts.static));
48
40
 
41
+ // Logger
49
42
  if (opts.logger) {
50
43
  arr.push(logger('dev', {
51
44
  skip: req => process.env.NODE_ENV === 'test' || req.path === '/favicon.ico'
52
45
  }));
53
- } // No cache for IE
54
- // https://support.microsoft.com/en-us/kb/234067
55
-
46
+ }
56
47
 
48
+ // No cache for IE
49
+ // https://support.microsoft.com/en-us/kb/234067
57
50
  arr.push((req, res, next) => {
58
51
  res.header('Cache-Control', 'no-cache');
59
52
  res.header('Pragma', 'no-cache');
60
53
  res.header('Expires', '-1');
61
54
  next();
62
- }); // Read-only
55
+ });
63
56
 
57
+ // Read-only
64
58
  if (opts.readOnly) {
65
59
  arr.push((req, res, next) => {
66
60
  if (req.method === 'GET') {
@@ -69,12 +63,11 @@ module.exports = function (opts) {
69
63
  res.sendStatus(403); // Forbidden
70
64
  }
71
65
  });
72
- } // Add middlewares
73
-
66
+ }
74
67
 
68
+ // Add middlewares
75
69
  if (opts.bodyParser) {
76
70
  arr.push(bodyParser);
77
71
  }
78
-
79
72
  return arr;
80
73
  };
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
 
3
3
  const express = require('express');
4
-
5
4
  module.exports = {
6
5
  create: () => express().set('json spaces', 2),
7
6
  defaults: require('./defaults'),
@@ -3,33 +3,29 @@
3
3
  const {
4
4
  nanoid
5
5
  } = require('nanoid');
6
-
7
6
  const pluralize = require('pluralize');
8
-
9
7
  module.exports = {
10
8
  getRemovable,
11
9
  createId,
12
10
  deepQuery
13
- }; // Returns document ids that have unsatisfied relations
14
- // Example: a comment that references a post that doesn't exist
11
+ };
15
12
 
13
+ // Returns document ids that have unsatisfied relations
14
+ // Example: a comment that references a post that doesn't exist
16
15
  function getRemovable(db, opts) {
17
16
  const _ = this;
18
-
19
17
  const removable = [];
20
-
21
18
  _.each(db, (coll, collName) => {
22
19
  _.each(coll, doc => {
23
20
  _.each(doc, (value, key) => {
24
21
  if (new RegExp(`${opts.foreignKeySuffix}$`).test(key)) {
25
22
  // Remove foreign key suffix and pluralize it
26
23
  // Example postId -> posts
27
- const refName = pluralize.plural(key.replace(new RegExp(`${opts.foreignKeySuffix}$`), '')); // Test if table exists
28
-
24
+ const refName = pluralize.plural(key.replace(new RegExp(`${opts.foreignKeySuffix}$`), ''));
25
+ // Test if table exists
29
26
  if (db[refName]) {
30
27
  // Test if references is defined in table
31
28
  const ref = _.getById(db[refName], value);
32
-
33
29
  if (_.isUndefined(ref)) {
34
30
  removable.push({
35
31
  name: collName,
@@ -41,30 +37,25 @@ function getRemovable(db, opts) {
41
37
  });
42
38
  });
43
39
  });
44
-
45
40
  return removable;
46
- } // Return incremented id or uuid
47
- // Used to override lodash-id's createId with utils.createId
48
-
41
+ }
49
42
 
43
+ // Return incremented id or uuid
44
+ // Used to override lodash-id's createId with utils.createId
50
45
  function createId(coll) {
51
46
  const _ = this;
52
-
53
47
  const idProperty = _.__id();
54
-
55
48
  if (_.isEmpty(coll)) {
56
49
  return 1;
57
50
  } else {
58
- let id = _(coll).maxBy(idProperty)[idProperty]; // Increment integer id or generate string id
59
-
51
+ let id = _(coll).maxBy(idProperty)[idProperty];
60
52
 
53
+ // Increment integer id or generate string id
61
54
  return _.isFinite(id) ? ++id : nanoid(7);
62
55
  }
63
56
  }
64
-
65
57
  function deepQuery(value, q) {
66
58
  const _ = this;
67
-
68
59
  if (value && q) {
69
60
  if (_.isArray(value)) {
70
61
  for (let i = 0; i < value.length; i++) {
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  const express = require('express');
4
-
5
4
  const rewrite = require('express-urlrewrite');
6
-
7
5
  module.exports = routes => {
8
6
  const router = express.Router();
9
7
  router.get('/__rules', (req, res) => {
@@ -1,12 +1,10 @@
1
1
  "use strict";
2
2
 
3
3
  const pause = require('connect-pause');
4
-
5
4
  module.exports = function delay(req, res, next) {
6
5
  // NOTE: for some reason unknown to me, if the default is 0, the tests seems to add 2 seconds
7
6
  // NOTE: to each test, a default value of 1 does not seem to be effected by that issue
8
7
  const _delay = !isNaN(parseFloat(req.query._delay)) ? parseFloat(req.query._delay) : 1;
9
-
10
8
  delete req.query._delay;
11
9
  pause(_delay)(req, res, next);
12
10
  };
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
 
3
3
  const url = require('url');
4
-
5
4
  module.exports = function getFullURL(req) {
6
5
  const root = url.format({
7
6
  protocol: req.protocol,
@@ -1,81 +1,75 @@
1
1
  "use strict";
2
2
 
3
3
  const express = require('express');
4
-
5
4
  const methodOverride = require('method-override');
6
-
7
5
  const _ = require('lodash');
8
-
9
6
  const lodashId = require('lodash-id');
10
-
11
7
  const low = require('lowdb');
12
-
13
8
  const Memory = require('lowdb/adapters/Memory');
14
-
15
9
  const FileSync = require('lowdb/adapters/FileSync');
16
-
17
10
  const bodyParser = require('../body-parser');
18
-
19
11
  const validateData = require('./validate-data');
20
-
21
12
  const plural = require('./plural');
22
-
23
13
  const nested = require('./nested');
24
-
25
14
  const singular = require('./singular');
26
-
27
15
  const mixins = require('../mixins');
28
-
29
16
  module.exports = (db, opts) => {
30
17
  opts = Object.assign({
31
18
  foreignKeySuffix: 'Id',
32
19
  _isFake: false
33
20
  }, opts);
34
-
35
21
  if (typeof db === 'string') {
36
22
  db = low(new FileSync(db));
37
23
  } else if (!_.has(db, '__chain__') || !_.has(db, '__wrapped__')) {
38
24
  db = low(new Memory()).setState(db);
39
- } // Create router
40
-
25
+ }
41
26
 
42
- const router = express.Router(); // Add middlewares
27
+ // Create router
28
+ const router = express.Router();
43
29
 
30
+ // Add middlewares
44
31
  router.use(methodOverride());
45
32
  router.use(bodyParser);
46
- validateData(db.getState()); // Add lodash-id methods to db
33
+ validateData(db.getState());
47
34
 
48
- db._.mixin(lodashId); // Add specific mixins
35
+ // Add lodash-id methods to db
36
+ db._.mixin(lodashId);
49
37
 
38
+ // Add specific mixins
39
+ db._.mixin(mixins);
50
40
 
51
- db._.mixin(mixins); // Expose database
52
-
53
-
54
- router.db = db; // Expose render
41
+ // Expose database
42
+ router.db = db;
55
43
 
44
+ // Expose render
56
45
  router.render = (req, res) => {
57
46
  res.jsonp(res.locals.data);
58
- }; // GET /db
59
-
47
+ };
60
48
 
49
+ // GET /db
61
50
  router.get('/db', (req, res) => {
62
51
  res.jsonp(db.getState());
63
- }); // Handle /:parent/:parentId/:resource
52
+ });
64
53
 
65
- router.use(nested(opts)); // Create routes
54
+ // Handle /:parent/:parentId/:resource
55
+ router.use(nested(opts));
66
56
 
57
+ // Create routes
67
58
  db.forEach((value, key) => {
59
+ if (key === '$schema') {
60
+ // ignore $schema
61
+ return;
62
+ }
68
63
  if (_.isPlainObject(value)) {
69
64
  router.use(`/${key}`, singular(db, key, opts));
70
65
  return;
71
66
  }
72
-
73
67
  if (_.isArray(value)) {
74
68
  router.use(`/${key}`, plural(db, key, opts));
75
69
  return;
76
70
  }
77
-
78
- const sourceMessage = ''; // if (!_.isObject(source)) {
71
+ const sourceMessage = '';
72
+ // if (!_.isObject(source)) {
79
73
  // sourceMessage = `in ${source}`
80
74
  // }
81
75
 
@@ -87,7 +81,6 @@ module.exports = (db, opts) => {
87
81
  res.status(404);
88
82
  res.locals.data = {};
89
83
  }
90
-
91
84
  router.render(req, res);
92
85
  });
93
86
  router.use((err, req, res, next) => {
@@ -1,29 +1,26 @@
1
1
  "use strict";
2
2
 
3
3
  const express = require('express');
4
-
5
4
  const pluralize = require('pluralize');
6
-
7
5
  const delay = require('./delay');
8
-
9
6
  module.exports = opts => {
10
7
  const router = express.Router();
11
- router.use(delay); // Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
8
+ router.use(delay);
12
9
 
10
+ // Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
13
11
  function get(req, res, next) {
14
12
  const prop = pluralize.singular(req.params.resource);
15
13
  req.query[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
16
14
  req.url = `/${req.params.nested}`;
17
15
  next();
18
- } // Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
19
-
16
+ }
20
17
 
18
+ // Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
21
19
  function post(req, res, next) {
22
20
  const prop = pluralize.singular(req.params.resource);
23
21
  req.body[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
24
22
  req.url = `/${req.params.nested}`;
25
23
  next();
26
24
  }
27
-
28
25
  return router.get('/:resource/:id/:nested', get).post('/:resource/:id/:nested', post);
29
26
  };
@@ -1,30 +1,23 @@
1
1
  "use strict";
2
2
 
3
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
4
-
5
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
6
-
7
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
8
-
3
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
7
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
9
8
  const express = require('express');
10
-
11
9
  const _ = require('lodash');
12
-
13
10
  const pluralize = require('pluralize');
14
-
15
11
  const write = require('./write');
16
-
17
12
  const getFullURL = require('./get-full-url');
18
-
19
13
  const utils = require('../utils');
20
-
21
14
  const delay = require('./delay');
22
-
23
15
  module.exports = (db, name, opts) => {
24
16
  // Create router
25
17
  const router = express.Router();
26
- router.use(delay); // Embed function used in GET /name and GET /name/id
18
+ router.use(delay);
27
19
 
20
+ // Embed function used in GET /name and GET /name/id
28
21
  function embed(resource, e) {
29
22
  e && [].concat(e).forEach(externalResource => {
30
23
  if (db.get(externalResource).value) {
@@ -34,31 +27,31 @@ module.exports = (db, name, opts) => {
34
27
  resource[externalResource] = db.get(externalResource).filter(query).value();
35
28
  }
36
29
  });
37
- } // Expand function used in GET /name and GET /name/id
38
-
30
+ }
39
31
 
32
+ // Expand function used in GET /name and GET /name/id
40
33
  function expand(resource, e) {
41
34
  e && [].concat(e).forEach(innerResource => {
42
35
  const plural = pluralize(innerResource);
43
-
44
36
  if (db.get(plural).value()) {
45
37
  const prop = `${innerResource}${opts.foreignKeySuffix}`;
46
38
  resource[innerResource] = db.get(plural).getById(resource[prop]).value();
47
39
  }
48
40
  });
49
- } // GET /name
41
+ }
42
+
43
+ // GET /name
50
44
  // GET /name?q=
51
45
  // GET /name?attr=&attr=
52
46
  // GET /name?_end=&
53
47
  // GET /name?_start=&_end=&
54
48
  // GET /name?_embed=&_expand=
55
-
56
-
57
49
  function list(req, res, next) {
58
50
  // Resource chain
59
- let chain = db.get(name); // Remove q, _start, _end, ... from req.query to avoid filtering using those
60
- // parameters
51
+ let chain = db.get(name);
61
52
 
53
+ // Remove q, _start, _end, ... from req.query to avoid filtering using those
54
+ // parameters
62
55
  let q = req.query.q;
63
56
  let _start = req.query._start;
64
57
  let _end = req.query._end;
@@ -75,39 +68,33 @@ module.exports = (db, name, opts) => {
75
68
  delete req.query._order;
76
69
  delete req.query._limit;
77
70
  delete req.query._embed;
78
- delete req.query._expand; // Automatically delete query parameters that can't be found
79
- // in the database
71
+ delete req.query._expand;
80
72
 
73
+ // Automatically delete query parameters that can't be found
74
+ // in the database
81
75
  Object.keys(req.query).forEach(query => {
82
76
  const arr = db.get(name).value();
83
-
84
77
  for (const i in arr) {
85
78
  if (_.has(arr[i], query) || query === 'callback' || query === '_' || /_lte$/.test(query) || /_gte$/.test(query) || /_ne$/.test(query) || /_like$/.test(query)) return;
86
79
  }
87
-
88
80
  delete req.query[query];
89
81
  });
90
-
91
82
  if (q) {
92
83
  // Full-text search
93
84
  if (Array.isArray(q)) {
94
85
  q = q[0];
95
86
  }
96
-
97
87
  q = q.toLowerCase();
98
88
  chain = chain.filter(obj => {
99
89
  for (const key in obj) {
100
90
  const value = obj[key];
101
-
102
91
  if (db._.deepQuery(value, q)) {
103
92
  return true;
104
93
  }
105
94
  }
106
-
107
95
  return false;
108
96
  });
109
97
  }
110
-
111
98
  Object.keys(req.query).forEach(key => {
112
99
  // Don't take into account JSONP query parameters
113
100
  // jQuery adds a '_' query parameter too
@@ -122,13 +109,12 @@ module.exports = (db, name, opts) => {
122
109
  return arr.map(function (value) {
123
110
  // get item value based on path
124
111
  // i.e post.title -> 'foo'
125
- const elementValue = _.get(element, path); // Prevent toString() failing on undefined or null values
126
-
112
+ const elementValue = _.get(element, path);
127
113
 
114
+ // Prevent toString() failing on undefined or null values
128
115
  if (elementValue === undefined || elementValue === null) {
129
116
  return undefined;
130
117
  }
131
-
132
118
  if (isRange) {
133
119
  const isLowerThan = /_gte$/.test(key);
134
120
  return isLowerThan ? value <= elementValue : value >= elementValue;
@@ -142,22 +128,20 @@ module.exports = (db, name, opts) => {
142
128
  }).reduce((a, b) => isDifferent ? a && b : a || b);
143
129
  });
144
130
  }
145
- }); // Sort
131
+ });
146
132
 
133
+ // Sort
147
134
  if (_sort) {
148
135
  const _sortSet = _sort.split(',');
149
-
150
136
  const _orderSet = (_order || '').split(',').map(s => s.toLowerCase());
151
-
152
137
  chain = chain.orderBy(_sortSet, _orderSet);
153
- } // Slice result
154
-
138
+ }
155
139
 
140
+ // Slice result
156
141
  if (_end || _limit || _page) {
157
142
  res.setHeader('X-Total-Count', chain.size());
158
143
  res.setHeader('Access-Control-Expose-Headers', `X-Total-Count${_page ? ', Link' : ''}`);
159
144
  }
160
-
161
145
  if (_page) {
162
146
  _page = parseInt(_page, 10);
163
147
  _page = _page >= 1 ? _page : 1;
@@ -165,23 +149,18 @@ module.exports = (db, name, opts) => {
165
149
  const page = utils.getPage(chain.value(), _page, _limit);
166
150
  const links = {};
167
151
  const fullURL = getFullURL(req);
168
-
169
152
  if (page.first) {
170
153
  links.first = fullURL.replace(`page=${page.current}`, `page=${page.first}`);
171
154
  }
172
-
173
155
  if (page.prev) {
174
156
  links.prev = fullURL.replace(`page=${page.current}`, `page=${page.prev}`);
175
157
  }
176
-
177
158
  if (page.next) {
178
159
  links.next = fullURL.replace(`page=${page.current}`, `page=${page.next}`);
179
160
  }
180
-
181
161
  if (page.last) {
182
162
  links.last = fullURL.replace(`page=${page.current}`, `page=${page.last}`);
183
163
  }
184
-
185
164
  res.links(links);
186
165
  chain = _.chain(page.items);
187
166
  } else if (_end) {
@@ -192,44 +171,42 @@ module.exports = (db, name, opts) => {
192
171
  _start = parseInt(_start, 10) || 0;
193
172
  _limit = parseInt(_limit, 10);
194
173
  chain = chain.slice(_start, _start + _limit);
195
- } // embed and expand
196
-
174
+ }
197
175
 
176
+ // embed and expand
198
177
  chain = chain.cloneDeep().forEach(function (element) {
199
178
  embed(element, _embed);
200
179
  expand(element, _expand);
201
180
  });
202
181
  res.locals.data = chain.value();
203
182
  next();
204
- } // GET /name/:id
205
- // GET /name/:id?_embed=&_expand
206
-
183
+ }
207
184
 
185
+ // GET /name/:id
186
+ // GET /name/:id?_embed=&_expand
208
187
  function show(req, res, next) {
209
188
  const _embed = req.query._embed;
210
189
  const _expand = req.query._expand;
211
190
  const resource = db.get(name).getById(req.params.id).value();
212
-
213
191
  if (resource) {
214
192
  // Clone resource to avoid making changes to the underlying object
215
- const clone = _.cloneDeep(resource); // Embed other resources based on resource id
216
- // /posts/1?_embed=comments
193
+ const clone = _.cloneDeep(resource);
217
194
 
195
+ // Embed other resources based on resource id
196
+ // /posts/1?_embed=comments
197
+ embed(clone, _embed);
218
198
 
219
- embed(clone, _embed); // Expand inner resources based on id
199
+ // Expand inner resources based on id
220
200
  // /posts/1?_expand=user
221
-
222
201
  expand(clone, _expand);
223
202
  res.locals.data = clone;
224
203
  }
225
-
226
204
  next();
227
- } // POST /name
228
-
205
+ }
229
206
 
207
+ // POST /name
230
208
  function create(req, res, next) {
231
209
  let resource;
232
-
233
210
  if (opts._isFake) {
234
211
  const id = db.get(name).createId().value();
235
212
  resource = _objectSpread(_objectSpread({}, req.body), {}, {
@@ -238,23 +215,20 @@ module.exports = (db, name, opts) => {
238
215
  } else {
239
216
  resource = db.get(name).insert(req.body).value();
240
217
  }
241
-
242
218
  res.setHeader('Access-Control-Expose-Headers', 'Location');
243
219
  res.location(`${getFullURL(req)}/${resource.id}`);
244
220
  res.status(201);
245
221
  res.locals.data = resource;
246
222
  next();
247
- } // PUT /name/:id
248
- // PATCH /name/:id
249
-
223
+ }
250
224
 
225
+ // PUT /name/:id
226
+ // PATCH /name/:id
251
227
  function update(req, res, next) {
252
228
  const id = req.params.id;
253
229
  let resource;
254
-
255
230
  if (opts._isFake) {
256
231
  resource = db.get(name).getById(id).value();
257
-
258
232
  if (req.method === 'PATCH') {
259
233
  resource = _objectSpread(_objectSpread({}, resource), req.body);
260
234
  } else {
@@ -267,37 +241,31 @@ module.exports = (db, name, opts) => {
267
241
  chain = req.method === 'PATCH' ? chain.updateById(id, req.body) : chain.replaceById(id, req.body);
268
242
  resource = chain.value();
269
243
  }
270
-
271
244
  if (resource) {
272
245
  res.locals.data = resource;
273
246
  }
274
-
275
247
  next();
276
- } // DELETE /name/:id
277
-
248
+ }
278
249
 
250
+ // DELETE /name/:id
279
251
  function destroy(req, res, next) {
280
252
  let resource;
281
-
282
253
  if (opts._isFake) {
283
254
  resource = db.get(name).value();
284
255
  } else {
285
- resource = db.get(name).removeById(req.params.id).value(); // Remove dependents documents
256
+ resource = db.get(name).removeById(req.params.id).value();
286
257
 
258
+ // Remove dependents documents
287
259
  const removable = db._.getRemovable(db.getState(), opts);
288
-
289
260
  removable.forEach(item => {
290
261
  db.get(item.name).removeById(item.id).value();
291
262
  });
292
263
  }
293
-
294
264
  if (resource) {
295
265
  res.locals.data = {};
296
266
  }
297
-
298
267
  next();
299
268
  }
300
-
301
269
  const w = write(db);
302
270
  router.route('/').get(list).post(create, w);
303
271
  router.route('/:id').get(show).put(update, w).patch(update, w).delete(destroy, w);
@@ -1,28 +1,21 @@
1
1
  "use strict";
2
2
 
3
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
4
-
5
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
6
-
7
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
8
-
3
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
7
+ function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
9
8
  const express = require('express');
10
-
11
9
  const write = require('./write');
12
-
13
10
  const getFullURL = require('./get-full-url');
14
-
15
11
  const delay = require('./delay');
16
-
17
12
  module.exports = (db, name, opts) => {
18
13
  const router = express.Router();
19
14
  router.use(delay);
20
-
21
15
  function show(req, res, next) {
22
16
  res.locals.data = db.get(name).value();
23
17
  next();
24
18
  }
25
-
26
19
  function create(req, res, next) {
27
20
  if (opts._isFake) {
28
21
  res.locals.data = req.body;
@@ -30,13 +23,11 @@ module.exports = (db, name, opts) => {
30
23
  db.set(name, req.body).value();
31
24
  res.locals.data = db.get(name).value();
32
25
  }
33
-
34
26
  res.setHeader('Access-Control-Expose-Headers', 'Location');
35
27
  res.location(`${getFullURL(req)}`);
36
28
  res.status(201);
37
29
  next();
38
30
  }
39
-
40
31
  function update(req, res, next) {
41
32
  if (opts._isFake) {
42
33
  if (req.method === 'PUT') {
@@ -51,13 +42,10 @@ module.exports = (db, name, opts) => {
51
42
  } else {
52
43
  db.get(name).assign(req.body).value();
53
44
  }
54
-
55
45
  res.locals.data = db.get(name).value();
56
46
  }
57
-
58
47
  next();
59
48
  }
60
-
61
49
  const w = write(db);
62
50
  router.route('/').get(show).post(create, w).put(update, w).patch(update, w);
63
51
  return router;
@@ -1,19 +1,17 @@
1
1
  "use strict";
2
2
 
3
3
  const _ = require('lodash');
4
-
5
4
  function validateKey(key) {
6
5
  if (key.indexOf('/') !== -1) {
7
6
  const msg = [`Oops, found / character in database property '${key}'.`, '', "/ aren't supported, if you want to tweak default routes, see", 'https://github.com/typicode/json-server/#add-custom-routes'].join('\n');
8
7
  throw new Error(msg);
9
8
  }
10
9
  }
11
-
12
10
  module.exports = obj => {
13
11
  if (_.isPlainObject(obj)) {
14
12
  Object.keys(obj).forEach(validateKey);
15
13
  } else {
16
- throw new Error(`Data must be an object. Found ${Array.isArray(obj) ? 'array' : typeof obj}
14
+ throw new Error(`Data must be an object. Found ${Array.isArray(obj) ? 'array' : typeof obj}.
17
15
  'See https://github.com/typicode/json-server for example.`);
18
16
  }
19
17
  };
@@ -3,30 +3,24 @@
3
3
  module.exports = {
4
4
  getPage
5
5
  };
6
-
7
6
  function getPage(array, page, perPage) {
8
7
  const obj = {};
9
8
  const start = (page - 1) * perPage;
10
9
  const end = page * perPage;
11
10
  obj.items = array.slice(start, end);
12
-
13
11
  if (obj.items.length === 0) {
14
12
  return obj;
15
13
  }
16
-
17
14
  if (page > 1) {
18
15
  obj.prev = page - 1;
19
16
  }
20
-
21
17
  if (end < array.length) {
22
18
  obj.next = page + 1;
23
19
  }
24
-
25
20
  if (obj.items.length !== array.length) {
26
21
  obj.current = page;
27
22
  obj.first = 1;
28
23
  obj.last = Math.ceil(array.length / perPage);
29
24
  }
30
-
31
25
  return obj;
32
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-server",
3
- "version": "0.17.2",
3
+ "version": "0.17.4",
4
4
  "description": "Get a full fake REST API with zero coding in less than 30 seconds",
5
5
  "main": "./lib/server/index.js",
6
6
  "bin": "./lib/cli/bin.js",
@@ -10,7 +10,7 @@
10
10
  ],
11
11
  "scripts": {
12
12
  "prepare": "husky install",
13
- "test": "npm run build && cross-env NODE_ENV=test jest && npm run lint",
13
+ "test": "npm run build && cross-env NODE_ENV=test jest",
14
14
  "start": "babel-node -- src/cli/bin db.json -r routes.json",
15
15
  "lint": "eslint . --ignore-path .gitignore",
16
16
  "fix": "npm run lint -- --fix",
@@ -38,7 +38,6 @@
38
38
  "please-upgrade-node": "^3.2.0",
39
39
  "pluralize": "^8.0.0",
40
40
  "server-destroy": "^1.0.1",
41
- "standard": "^17.0.0",
42
41
  "yargs": "^17.0.1"
43
42
  },
44
43
  "devDependencies": {
@@ -64,6 +63,7 @@
64
63
  "prettier": "^2.3.2",
65
64
  "rimraf": "^3.0.2",
66
65
  "server-ready": "^0.3.1",
66
+ "standard": "^17.1.0",
67
67
  "supertest": "^6.0.1",
68
68
  "temp-write": "^4.0.0"
69
69
  },