mirror of
https://github.com/typicode/json-server.git
synced 2025-07-28 12:43:18 +08:00
Refactor and update to LowDB 0.4
This commit is contained in:
36
README.md
36
README.md
@ -16,7 +16,7 @@ _Powers http://jsonplaceholder.typicode.com_
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// db.json
|
// db.json
|
||||||
{
|
{
|
||||||
"posts": [
|
"posts": [
|
||||||
{ "id": 1, "body": "foo" }
|
{ "id": 1, "body": "foo" }
|
||||||
]
|
]
|
||||||
@ -33,13 +33,11 @@ $ curl -i http://localhost:3000/posts/1
|
|||||||
```javascript
|
```javascript
|
||||||
var server = require('json-server');
|
var server = require('json-server');
|
||||||
|
|
||||||
server.low.db = {
|
server({
|
||||||
posts: [
|
posts: [
|
||||||
{ id: 1, body: 'foo' }
|
{ id: 1, body: 'foo' }
|
||||||
]
|
]
|
||||||
}
|
}).listen(3000);
|
||||||
|
|
||||||
server.listen(3000);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can find a running demo here: http://jsonplaceholder.typicode.com.
|
You can find a running demo here: http://jsonplaceholder.typicode.com.
|
||||||
@ -62,20 +60,18 @@ $ npm install -g json-server
|
|||||||
## CLI usage
|
## CLI usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
json-server <source>
|
||||||
|
|
||||||
Usage: json-server <source> [options]
|
Examples:
|
||||||
|
json-server db.json
|
||||||
|
json-server file.js
|
||||||
|
json-server http://example.com/db.json
|
||||||
|
|
||||||
Options:
|
|
||||||
|
|
||||||
--version output version
|
Options:
|
||||||
--port <port> set port
|
--help, -h Show help
|
||||||
|
--version, -v Show version number
|
||||||
Exemples:
|
--port, -p Set port [default: 3000]
|
||||||
|
|
||||||
json-server db.json
|
|
||||||
json-server seed.js
|
|
||||||
json-server http://example.com/db.json
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Input
|
#### Input
|
||||||
@ -97,10 +93,10 @@ Here's 2 examples showing how to format JSON or JS seed file:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* __seed.js__
|
* __file.js__
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
exports.run = function() {
|
module.exports = function() {
|
||||||
var data = {};
|
var data = {};
|
||||||
|
|
||||||
data.posts = [];
|
data.posts = [];
|
||||||
@ -111,7 +107,7 @@ exports.run = function() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
JSON Server expects JS files to export a ```run``` method that returns an object.
|
JSON Server expects JS files to export a function that returns an object.
|
||||||
|
|
||||||
Seed files are useful if you need to programmaticaly create a lot of data.
|
Seed files are useful if you need to programmaticaly create a lot of data.
|
||||||
|
|
||||||
@ -159,4 +155,4 @@ Returns default index file or content of ./public/index.html (useful if you need
|
|||||||
## Links
|
## Links
|
||||||
|
|
||||||
* [Fast prototyping using Restangular and Json-server](http://bahmutov.calepin.co/fast-prototyping-using-restangular-and-json-server.html)
|
* [Fast prototyping using Restangular and Json-server](http://bahmutov.calepin.co/fast-prototyping-using-restangular-and-json-server.html)
|
||||||
* [Grunt plugin](https://github.com/tfiwm/grunt-json-server)
|
* [grunt plugin](https://github.com/tfiwm/grunt-json-server)
|
82
bin/index.js
82
bin/index.js
@ -1,20 +1,74 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
var minimist = require('minimist')
|
|
||||||
var updateNotifier = require('update-notifier')
|
var updateNotifier = require('update-notifier')
|
||||||
var cli = require('../src/cli')
|
var _db = require('underscore-db')
|
||||||
|
var yargs = require('yargs')
|
||||||
|
var chalk = require('chalk')
|
||||||
|
var got = require('got')
|
||||||
|
var pkg = require('../package.json')
|
||||||
|
var server = require('../src')
|
||||||
|
|
||||||
var notifier = updateNotifier({packagePath: '../package'})
|
updateNotifier({packageName: pkg.name, packageVersion: pkg.version}).notify()
|
||||||
if (notifier.update) notifier.notify()
|
|
||||||
|
|
||||||
var argv = minimist(process.argv.slice(2), {
|
var argv = yargs
|
||||||
boolean: ["silent"],
|
.usage('$0 <source>')
|
||||||
alias: {
|
.help('help').alias('help', 'h')
|
||||||
'p': 'port',
|
.version(pkg.version, 'version').alias('version', 'v')
|
||||||
's': 'silent'
|
.options({
|
||||||
},
|
port: {
|
||||||
default: {
|
alias: 'p',
|
||||||
silent: false
|
description: 'Set port',
|
||||||
|
default: 3000
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.example('$0 db.json', '')
|
||||||
|
.example('$0 file.js', '')
|
||||||
|
.example('$0 http://example.com/db.json', '')
|
||||||
|
.require(1, 'Missing <source> argument')
|
||||||
|
.argv
|
||||||
|
|
||||||
cli.run(argv)
|
function start(object, filename) {
|
||||||
|
for (var prop in object) {
|
||||||
|
console.log('http://localhost:' + port + '/' + chalk.green(prop))
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'\nEnter ' + chalk.green('`s`') + ' at any time to create a snapshot of the db\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
process.stdin.resume()
|
||||||
|
process.stdin.setEncoding('utf8')
|
||||||
|
process.stdin.on('data', function (chunk) {
|
||||||
|
if (chunk.trim().toLowerCase() === 's') {
|
||||||
|
var file = 'db-' + Date.now() + '.json'
|
||||||
|
_db.save(object, filename)
|
||||||
|
console.log('\nSaved snapshot to ' + chalk.green(file) + '\n')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
server(object, filename).listen(port)
|
||||||
|
}
|
||||||
|
|
||||||
|
var source = argv._[0]
|
||||||
|
var port = process.env.PORT || argv.port
|
||||||
|
|
||||||
|
console.log(chalk.green('\n{^ ^} Yo!\n'))
|
||||||
|
console.log('Loading database from ' + source + '\n')
|
||||||
|
|
||||||
|
if (/\.json$/.test(source)) {
|
||||||
|
var filename = process.cwd() + '/' + source
|
||||||
|
var object = require(filename)
|
||||||
|
start(object, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/\.js$/.test(source)) {
|
||||||
|
var object = require(process.cwd() + '/' + source)()
|
||||||
|
start(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^http/.test(source)) {
|
||||||
|
got(source, function(err, data) {
|
||||||
|
if (err) throw err
|
||||||
|
var object = JSON.parse(data)
|
||||||
|
start(object)
|
||||||
|
})
|
||||||
|
}
|
15
package.json
15
package.json
@ -12,17 +12,18 @@
|
|||||||
"chalk": "^0.4.0",
|
"chalk": "^0.4.0",
|
||||||
"cors": "^2.3.0",
|
"cors": "^2.3.0",
|
||||||
"errorhandler": "^1.2.0",
|
"errorhandler": "^1.2.0",
|
||||||
"express": "^4.9.0",
|
"express": "^4.9.5",
|
||||||
"lowdb": "^0.3.0",
|
"got": "^1.2.2",
|
||||||
|
"lowdb": "^0.4.2",
|
||||||
"method-override": "^2.1.2",
|
"method-override": "^2.1.2",
|
||||||
"minimist": "0.0.8",
|
|
||||||
"morgan": "^1.3.1",
|
"morgan": "^1.3.1",
|
||||||
"serve-static": "^1.6.1",
|
"serve-static": "^1.6.1",
|
||||||
"superagent": "~0.15.7",
|
"superagent": "^0.15.7",
|
||||||
"underscore": "~1.5.2",
|
"underscore": "^1.5.2",
|
||||||
|
"underscore-db": "^0.8.0",
|
||||||
"underscore.inflections": "~0.2.1",
|
"underscore.inflections": "~0.2.1",
|
||||||
"update-notifier": "^0.1.8",
|
"update-notifier": "^0.2.2",
|
||||||
"yargs": "^1.2.1"
|
"yargs": "^1.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"supertest": "~0.8.1",
|
"supertest": "~0.8.1",
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4>Issues</h4>
|
<h4>Issues</h4>
|
||||||
<p>Please go
|
<p>Please go
|
||||||
<a href="https://github.com/typicode/jsonplaceholder/issues">here</a>.
|
<a href="https://github.com/typicode/jsonplaceholder/issues">here</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
90
src/cli.js
90
src/cli.js
@ -1,90 +0,0 @@
|
|||||||
var fs = require('fs')
|
|
||||||
var chalk = require('chalk')
|
|
||||||
var minimist = require('minimist')
|
|
||||||
var request = require('superagent')
|
|
||||||
var low = require('lowdb')
|
|
||||||
var server = require('./server')
|
|
||||||
|
|
||||||
// Output version
|
|
||||||
function version() {
|
|
||||||
var pkg = require('../package.json')
|
|
||||||
console.log(pkg.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output help.txt with some colors
|
|
||||||
function help() {
|
|
||||||
var txt = fs.readFileSync(__dirname + '/help.txt').toString()
|
|
||||||
txt = txt.replace(/json-server/g, chalk.green('json-server'))
|
|
||||||
console.log(txt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start server
|
|
||||||
function start(port, silent) {
|
|
||||||
for (var prop in low.db) {
|
|
||||||
console.log('http://localhost:' + port + '/' + chalk.green(prop))
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!silent) {
|
|
||||||
console.log(
|
|
||||||
'\nEnter ' + chalk.green('`s`') + ' at any time to create a snapshot of the db\n'
|
|
||||||
)
|
|
||||||
process.stdin.resume()
|
|
||||||
process.stdin.setEncoding('utf8')
|
|
||||||
process.stdin.on('data', function (chunk) {
|
|
||||||
if (chunk.trim().toLowerCase() === 's') {
|
|
||||||
var file = 'db-' + Date.now() + '.json'
|
|
||||||
low.save(file)
|
|
||||||
console.log('\nSaved snapshot to ' + chalk.green(file) + '\n')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
server.listen(port)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load source
|
|
||||||
function load(source, port, silent) {
|
|
||||||
console.log(chalk.green('\n{^ ^} Heya!\n'))
|
|
||||||
|
|
||||||
console.log('Loading database from ' + source + '\n')
|
|
||||||
|
|
||||||
if (/\.json$/.test(source)) {
|
|
||||||
var path = process.cwd() + '/' + source
|
|
||||||
low.path = path
|
|
||||||
low.db = require(path);
|
|
||||||
start(port, silent)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/\.js$/.test(source)) {
|
|
||||||
var path = process.cwd() + '/' + source
|
|
||||||
low.db = require(path).run();
|
|
||||||
start(port, silent)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/^http/.test(source)) {
|
|
||||||
request
|
|
||||||
.get(source)
|
|
||||||
.end(function(err, res) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err)
|
|
||||||
} else {
|
|
||||||
low.db = JSON.parse(res.text)
|
|
||||||
start(port, silent)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uses minimist parsed argv
|
|
||||||
function run(argv) {
|
|
||||||
var source = argv._[0]
|
|
||||||
var port = argv.port || 3000
|
|
||||||
var silent = argv.silent
|
|
||||||
|
|
||||||
if (argv.version) return version()
|
|
||||||
if (source) return load(source, port, silent)
|
|
||||||
|
|
||||||
help()
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.run = run
|
|
169
src/create-routes.js
Normal file
169
src/create-routes.js
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
var _ = require('underscore')
|
||||||
|
var low = require('lowdb')
|
||||||
|
var _db = require('underscore-db')
|
||||||
|
var _inflections = require('underscore.inflections')
|
||||||
|
var utils = require('./utils')
|
||||||
|
|
||||||
|
low.mixin(_db)
|
||||||
|
low.mixin(_inflections)
|
||||||
|
|
||||||
|
module.exports = function(object, filename) {
|
||||||
|
if (filename) {
|
||||||
|
var db = low(filename)
|
||||||
|
} else {
|
||||||
|
var db = low()
|
||||||
|
_.extend(db.object, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// GET /db
|
||||||
|
showDatabase: function(req, res, next) {
|
||||||
|
res.jsonp(db.object)
|
||||||
|
},
|
||||||
|
|
||||||
|
// GET /:resource
|
||||||
|
// GET /:resource?q=
|
||||||
|
// GET /:resource?attr=&attr=
|
||||||
|
// GET /:parent/:parentId/:resource?attr=&attr=
|
||||||
|
// GET /*?*&_end=
|
||||||
|
// GET /*?*&_start=&_end=
|
||||||
|
list: function(req, res, next) {
|
||||||
|
// Filters list
|
||||||
|
var filters = {}
|
||||||
|
|
||||||
|
// Result array
|
||||||
|
var array
|
||||||
|
|
||||||
|
// Remove _start and _end from req.query to avoid filtering using those
|
||||||
|
// parameters
|
||||||
|
var _start = req.query._start
|
||||||
|
var _end = req.query._end
|
||||||
|
var _sort = req.query._sort
|
||||||
|
var _sortDir = req.query._sortDir
|
||||||
|
|
||||||
|
delete req.query._start
|
||||||
|
delete req.query._end
|
||||||
|
delete req.query._sort
|
||||||
|
delete req.query._sortDir
|
||||||
|
|
||||||
|
if (req.query.q) {
|
||||||
|
|
||||||
|
// Full-text search
|
||||||
|
var q = req.query.q.toLowerCase()
|
||||||
|
|
||||||
|
array = db(req.params.resource).where(function(obj) {
|
||||||
|
for (var key in obj) {
|
||||||
|
var value = obj[key]
|
||||||
|
if (_.isString(value) && value.toLowerCase().indexOf(q) !== -1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).value()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Add :parentId filter in case URL is like /:parent/:parentId/:resource
|
||||||
|
if (req.params.parent) {
|
||||||
|
filters[req.params.parent.slice(0, - 1) + 'Id'] = +req.params.parentId
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add query parameters filters
|
||||||
|
// Convert query parameters to their native counterparts
|
||||||
|
for (var key in req.query) {
|
||||||
|
if (key !== 'callback') {
|
||||||
|
filters[key] = utils.toNative(req.query[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter
|
||||||
|
if (_(filters).isEmpty()) {
|
||||||
|
array = db(req.params.resource).value()
|
||||||
|
} else {
|
||||||
|
array = db(req.params.resource).where(filters).value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_sort) {
|
||||||
|
_sortDir = _sortDir || 'ASC'
|
||||||
|
|
||||||
|
array = _.sortBy(array, function(element) {
|
||||||
|
return element[_sort];
|
||||||
|
})
|
||||||
|
|
||||||
|
if (_sortDir === 'DESC') {
|
||||||
|
array.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice result
|
||||||
|
if (_end) {
|
||||||
|
res.setHeader('X-Total-Count', array.length)
|
||||||
|
res.setHeader('Access-Control-Expose-Headers', 'X-Total-Count')
|
||||||
|
|
||||||
|
_start = _start || 0
|
||||||
|
|
||||||
|
array = array.slice(_start, _end)
|
||||||
|
}
|
||||||
|
|
||||||
|
res.jsonp(array)
|
||||||
|
},
|
||||||
|
|
||||||
|
// GET /:resource/:id
|
||||||
|
show: function(req, res, next) {
|
||||||
|
var resource = db(req.params.resource)
|
||||||
|
.get(+req.params.id)
|
||||||
|
.value()
|
||||||
|
|
||||||
|
if (resource) {
|
||||||
|
res.jsonp(resource)
|
||||||
|
} else {
|
||||||
|
res.status(404).jsonp({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// POST /:resource
|
||||||
|
create: function(req, res, next) {
|
||||||
|
for (var key in req.body) {
|
||||||
|
req.body[key] = utils.toNative(req.body[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
var resource = db(req.params.resource)
|
||||||
|
.insert(req.body)
|
||||||
|
.value()
|
||||||
|
|
||||||
|
res.jsonp(resource)
|
||||||
|
},
|
||||||
|
|
||||||
|
// PUT /:resource/:id
|
||||||
|
// PATCH /:resource/:id
|
||||||
|
update: function(req, res, next) {
|
||||||
|
for (var key in req.body) {
|
||||||
|
req.body[key] = utils.toNative(req.body[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
var resource = db(req.params.resource)
|
||||||
|
.update(+req.params.id, req.body)
|
||||||
|
.value()
|
||||||
|
|
||||||
|
if (resource) {
|
||||||
|
res.jsonp(resource)
|
||||||
|
} else {
|
||||||
|
res.status(404).jsonp({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// DELETE /:resource/:id
|
||||||
|
destroy: function(req, res, next) {
|
||||||
|
db(req.params.resource).remove(+req.params.id)
|
||||||
|
|
||||||
|
// Remove dependents documents
|
||||||
|
var removable = utils.getRemovable(db.object)
|
||||||
|
|
||||||
|
_(removable).each(function(item) {
|
||||||
|
db(item.name).remove(item.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(204).end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
src/help.txt
14
src/help.txt
@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
Usage: json-server <source> [options]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
|
|
||||||
--version output version
|
|
||||||
--port <port> set port
|
|
||||||
|
|
||||||
Exemples:
|
|
||||||
|
|
||||||
json-server db.json
|
|
||||||
json-server seed.js
|
|
||||||
json-server http://example.com/db.json
|
|
||||||
|
|
69
src/index.js
Normal file
69
src/index.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
var fs = require('fs')
|
||||||
|
var path = require('path')
|
||||||
|
|
||||||
|
// LowDB
|
||||||
|
var low = require('lowdb')
|
||||||
|
var _db = require('underscore-db')
|
||||||
|
var _inflections = require('underscore.inflections')
|
||||||
|
|
||||||
|
low.mixin('_db')
|
||||||
|
low.mixin('_inflections')
|
||||||
|
|
||||||
|
// Express
|
||||||
|
var http = require('http')
|
||||||
|
var express = require('express')
|
||||||
|
var logger = require('morgan')
|
||||||
|
var cors = require('cors')
|
||||||
|
var methodOverride = require('method-override')
|
||||||
|
var bodyParser = require('body-parser')
|
||||||
|
var serveStatic = require('serve-static')
|
||||||
|
var errorhandler = require('errorhandler')
|
||||||
|
|
||||||
|
// json-server
|
||||||
|
var utils = require('./utils')
|
||||||
|
var createRoutes = require('./create-routes')
|
||||||
|
|
||||||
|
low.mixin({ createId: utils.createId })
|
||||||
|
|
||||||
|
module.exports = function(object, filename) {
|
||||||
|
var server = express()
|
||||||
|
var routes = createRoutes(object, filename)
|
||||||
|
|
||||||
|
// Don't use logger if json-server is mounted
|
||||||
|
if (!module.parent) {
|
||||||
|
server.use(logger('dev'))
|
||||||
|
}
|
||||||
|
|
||||||
|
server.use(bodyParser.json())
|
||||||
|
server.use(bodyParser.urlencoded({ extended: false }))
|
||||||
|
server.use(methodOverride())
|
||||||
|
|
||||||
|
if (fs.existsSync(process.cwd() + '/public')) {
|
||||||
|
server.use(serveStatic(process.cwd() + '/public'));
|
||||||
|
} else {
|
||||||
|
server.use(serveStatic(path.join(__dirname, './public')));
|
||||||
|
}
|
||||||
|
|
||||||
|
server.use(cors({ origin: true, credentials: true }))
|
||||||
|
|
||||||
|
server.get('/db', routes.showDatabase)
|
||||||
|
|
||||||
|
server.route('/:resource')
|
||||||
|
.get(routes.list)
|
||||||
|
.post(routes.create)
|
||||||
|
|
||||||
|
server.route('/:resource/:id')
|
||||||
|
.get(routes.show)
|
||||||
|
.put(routes.update)
|
||||||
|
.patch(routes.update)
|
||||||
|
.delete(routes.destroy)
|
||||||
|
|
||||||
|
server.get('/:parent/:parentId/:resource', routes.list)
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
// only use in development
|
||||||
|
server.use(errorhandler())
|
||||||
|
}
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
158
src/routes.js
158
src/routes.js
@ -1,158 +0,0 @@
|
|||||||
var _ = require('underscore')
|
|
||||||
var low = require('lowdb')
|
|
||||||
var utils = require('./utils')
|
|
||||||
|
|
||||||
var routes = {}
|
|
||||||
|
|
||||||
// GET /db
|
|
||||||
routes.db = function(req, res, next) {
|
|
||||||
res.jsonp(low.db)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET /:resource
|
|
||||||
// GET /:resource?q=
|
|
||||||
// GET /:resource?attr=&attr=
|
|
||||||
// GET /:parent/:parentId/:resource?attr=&attr=
|
|
||||||
// GET /*?*&_end=
|
|
||||||
// GET /*?*&_start=&_end=
|
|
||||||
routes.list = function(req, res, next) {
|
|
||||||
|
|
||||||
// Filters list
|
|
||||||
var filters = {}
|
|
||||||
|
|
||||||
// Result array
|
|
||||||
var array
|
|
||||||
|
|
||||||
// Remove _start and _end from req.query to avoid filtering using those
|
|
||||||
// parameters
|
|
||||||
var _start = req.query._start
|
|
||||||
var _end = req.query._end
|
|
||||||
var _sort = req.query._sort
|
|
||||||
var _sortDir = req.query._sortDir
|
|
||||||
|
|
||||||
delete req.query._start
|
|
||||||
delete req.query._end
|
|
||||||
delete req.query._sort
|
|
||||||
delete req.query._sortDir
|
|
||||||
|
|
||||||
if (req.query.q) {
|
|
||||||
|
|
||||||
// Full-text search
|
|
||||||
var q = req.query.q.toLowerCase()
|
|
||||||
|
|
||||||
array = low(req.params.resource).where(function(obj) {
|
|
||||||
for (var key in obj) {
|
|
||||||
var value = obj[key]
|
|
||||||
if (_.isString(value) && value.toLowerCase().indexOf(q) !== -1) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).value()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Add :parentId filter in case URL is like /:parent/:parentId/:resource
|
|
||||||
if (req.params.parent) {
|
|
||||||
filters[req.params.parent.slice(0, - 1) + 'Id'] = +req.params.parentId
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add query parameters filters
|
|
||||||
// Convert query parameters to their native counterparts
|
|
||||||
for (var key in req.query) {
|
|
||||||
if (key !== 'callback') {
|
|
||||||
filters[key] = utils.toNative(req.query[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter
|
|
||||||
if (_(filters).isEmpty()) {
|
|
||||||
array = low(req.params.resource).value()
|
|
||||||
} else {
|
|
||||||
array = low(req.params.resource).where(filters).value()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_sort) {
|
|
||||||
_sortDir = _sortDir || 'ASC'
|
|
||||||
|
|
||||||
array = _.sortBy(array, function(element) {
|
|
||||||
return element[_sort];
|
|
||||||
})
|
|
||||||
|
|
||||||
if (_sortDir === 'DESC') {
|
|
||||||
array.reverse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slice result
|
|
||||||
if (_end) {
|
|
||||||
res.setHeader('X-Total-Count', array.length)
|
|
||||||
res.setHeader('Access-Control-Expose-Headers', 'X-Total-Count')
|
|
||||||
|
|
||||||
_start = _start || 0
|
|
||||||
|
|
||||||
array = array.slice(_start, _end)
|
|
||||||
}
|
|
||||||
|
|
||||||
res.jsonp(array)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET /:resource/:id
|
|
||||||
routes.show = function(req, res, next) {
|
|
||||||
var resource = low(req.params.resource)
|
|
||||||
.get(+req.params.id)
|
|
||||||
.value()
|
|
||||||
|
|
||||||
if (resource) {
|
|
||||||
res.jsonp(resource)
|
|
||||||
} else {
|
|
||||||
res.status(404).jsonp({})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST /:resource
|
|
||||||
routes.create = function(req, res, next) {
|
|
||||||
for (var key in req.body) {
|
|
||||||
req.body[key] = utils.toNative(req.body[key])
|
|
||||||
}
|
|
||||||
|
|
||||||
var resource = low(req.params.resource)
|
|
||||||
.insert(req.body)
|
|
||||||
.value()
|
|
||||||
|
|
||||||
res.jsonp(resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUT /:resource/:id
|
|
||||||
// PATCH /:resource/:id
|
|
||||||
routes.update = function(req, res, next) {
|
|
||||||
for (var key in req.body) {
|
|
||||||
req.body[key] = utils.toNative(req.body[key])
|
|
||||||
}
|
|
||||||
|
|
||||||
var resource = low(req.params.resource)
|
|
||||||
.update(+req.params.id, req.body)
|
|
||||||
.value()
|
|
||||||
|
|
||||||
if (resource) {
|
|
||||||
res.jsonp(resource)
|
|
||||||
} else {
|
|
||||||
res.status(404).jsonp({})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DELETE /:resource/:id
|
|
||||||
routes.destroy = function(req, res, next) {
|
|
||||||
low(req.params.resource).remove(+req.params.id)
|
|
||||||
|
|
||||||
// Remove dependents documents
|
|
||||||
var removable = utils.getRemovable(low.db)
|
|
||||||
|
|
||||||
_(removable).each(function(item) {
|
|
||||||
low(item[0]).remove(item[1]);
|
|
||||||
})
|
|
||||||
|
|
||||||
res.status(204).end()
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = routes
|
|
@ -1,60 +0,0 @@
|
|||||||
var fs = require('fs')
|
|
||||||
var path = require('path')
|
|
||||||
var http = require('http')
|
|
||||||
var express = require('express')
|
|
||||||
var logger = require('morgan')
|
|
||||||
var cors = require('cors')
|
|
||||||
var methodOverride = require('method-override')
|
|
||||||
var bodyParser = require('body-parser')
|
|
||||||
var serveStatic = require('serve-static')
|
|
||||||
var errorhandler = require('errorhandler')
|
|
||||||
var low = require('lowdb')
|
|
||||||
|
|
||||||
var utils = require('./utils')
|
|
||||||
var routes = require('./routes')
|
|
||||||
|
|
||||||
low._.createId = utils.createId
|
|
||||||
|
|
||||||
var server = express()
|
|
||||||
|
|
||||||
server.set('port', process.env.PORT || 3000)
|
|
||||||
|
|
||||||
// Don't use logger if json-server is mounted
|
|
||||||
if (!module.parent) {
|
|
||||||
server.use(logger('dev'))
|
|
||||||
}
|
|
||||||
|
|
||||||
server.use(bodyParser.json())
|
|
||||||
server.use(bodyParser.urlencoded({ extended: false }))
|
|
||||||
server.use(methodOverride())
|
|
||||||
|
|
||||||
if (fs.existsSync(process.cwd() + '/public')) {
|
|
||||||
server.use(serveStatic(process.cwd() + '/public'));
|
|
||||||
} else {
|
|
||||||
server.use(serveStatic(path.join(__dirname, './public')));
|
|
||||||
}
|
|
||||||
|
|
||||||
server.use(cors({ origin: true, credentials: true }))
|
|
||||||
|
|
||||||
server.get('/db', routes.db)
|
|
||||||
|
|
||||||
server.route('/:resource')
|
|
||||||
.get(routes.list)
|
|
||||||
.post(routes.create)
|
|
||||||
|
|
||||||
server.route('/:resource/:id')
|
|
||||||
.get(routes.show)
|
|
||||||
.put(routes.update)
|
|
||||||
.patch(routes.update)
|
|
||||||
.delete(routes.destroy)
|
|
||||||
|
|
||||||
server.get('/:parent/:parentId/:resource', routes.list)
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
// only use in development
|
|
||||||
server.use(errorhandler())
|
|
||||||
}
|
|
||||||
|
|
||||||
server.low = low
|
|
||||||
|
|
||||||
module.exports = server
|
|
@ -1,7 +1,6 @@
|
|||||||
var low = require('lowdb')
|
|
||||||
var _ = require('underscore')
|
var _ = require('underscore')
|
||||||
_.mixin(require('underscore.inflections'))
|
var _inflections = require('underscore.inflections')
|
||||||
|
_.mixin(_inflections)
|
||||||
|
|
||||||
// Turns string to native.
|
// Turns string to native.
|
||||||
// Example:
|
// Example:
|
||||||
@ -42,7 +41,7 @@ function getRemovable(db) {
|
|||||||
var refName = _.pluralize(key.slice(0, - 2))
|
var refName = _.pluralize(key.slice(0, - 2))
|
||||||
var ref = _.findWhere(db[refName], {id: value})
|
var ref = _.findWhere(db[refName], {id: value})
|
||||||
if (_.isUndefined(ref)) {
|
if (_.isUndefined(ref)) {
|
||||||
removable.push([collName, doc.id])
|
removable.push({ name: collName, id: doc.id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,31 +1,35 @@
|
|||||||
var request = require('supertest')
|
var request = require('supertest')
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
var low = require('lowdb')
|
var jsonServer = require('../src/')
|
||||||
var server = require('../src/server')
|
|
||||||
|
|
||||||
describe('Server', function() {
|
describe('Server', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
var server
|
||||||
low.db = {}
|
var db
|
||||||
|
|
||||||
low.db.posts = [
|
beforeEach(function() {
|
||||||
|
db = {}
|
||||||
|
|
||||||
|
db.posts = [
|
||||||
{id: 1, body: 'foo'},
|
{id: 1, body: 'foo'},
|
||||||
{id: 2, body: 'bar'}
|
{id: 2, body: 'bar'}
|
||||||
]
|
]
|
||||||
|
|
||||||
low.db.tags = [
|
db.tags = [
|
||||||
{id: 1, body: 'Technology'},
|
{id: 1, body: 'Technology'},
|
||||||
{id: 2, body: 'Photography'},
|
{id: 2, body: 'Photography'},
|
||||||
{id: 3, body: 'photo'}
|
{id: 3, body: 'photo'}
|
||||||
]
|
]
|
||||||
|
|
||||||
low.db.comments = [
|
db.comments = [
|
||||||
{id: 1, published: true, postId: 1},
|
{id: 1, published: true, postId: 1},
|
||||||
{id: 2, published: false, postId: 1},
|
{id: 2, published: false, postId: 1},
|
||||||
{id: 3, published: false, postId: 2},
|
{id: 3, published: false, postId: 2},
|
||||||
{id: 4, published: false, postId: 2},
|
{id: 4, published: false, postId: 2},
|
||||||
{id: 5, published: false, postId: 2},
|
{id: 5, published: false, postId: 2},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
server = jsonServer(db)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('GET /db', function() {
|
describe('GET /db', function() {
|
||||||
@ -33,7 +37,7 @@ describe('Server', function() {
|
|||||||
request(server)
|
request(server)
|
||||||
.get('/db')
|
.get('/db')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(low.db)
|
.expect(db)
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -46,7 +50,7 @@ describe('Server', function() {
|
|||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect('Access-Control-Allow-Credentials', 'true')
|
.expect('Access-Control-Allow-Credentials', 'true')
|
||||||
.expect('Access-Control-Allow-Origin', 'http://example.com')
|
.expect('Access-Control-Allow-Origin', 'http://example.com')
|
||||||
.expect(low.db.posts)
|
.expect(db.posts)
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -56,7 +60,7 @@ describe('Server', function() {
|
|||||||
request(server)
|
request(server)
|
||||||
.get('/comments?postId=1&published=true')
|
.get('/comments?postId=1&published=true')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect([low.db.comments[0]])
|
.expect([db.comments[0]])
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -66,7 +70,7 @@ describe('Server', function() {
|
|||||||
request(server)
|
request(server)
|
||||||
.get('/tags?q=pho')
|
.get('/tags?q=pho')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect([low.db.tags[1], low.db.tags[2]])
|
.expect([db.tags[1], db.tags[2]])
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -84,9 +88,9 @@ describe('Server', function() {
|
|||||||
request(server)
|
request(server)
|
||||||
.get('/comments?_end=2')
|
.get('/comments?_end=2')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect('x-total-count', low.db.comments.length.toString())
|
.expect('x-total-count', db.comments.length.toString())
|
||||||
.expect('Access-Control-Expose-Headers', 'X-Total-Count')
|
.expect('Access-Control-Expose-Headers', 'X-Total-Count')
|
||||||
.expect(low.db.comments.slice(0, 2))
|
.expect(db.comments.slice(0, 2))
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -96,7 +100,7 @@ describe('Server', function() {
|
|||||||
request(server)
|
request(server)
|
||||||
.get('/tags?_sort=body')
|
.get('/tags?_sort=body')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect([low.db.tags[1], low.db.tags[0], low.db.tags[2]])
|
.expect([db.tags[1], db.tags[0], db.tags[2]])
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -104,7 +108,7 @@ describe('Server', function() {
|
|||||||
request(server)
|
request(server)
|
||||||
.get('/tags?_sort=body&_sortDir=DESC')
|
.get('/tags?_sort=body&_sortDir=DESC')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect([low.db.tags[2], low.db.tags[0], low.db.tags[1]])
|
.expect([db.tags[2], db.tags[0], db.tags[1]])
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -112,7 +116,7 @@ describe('Server', function() {
|
|||||||
request(server)
|
request(server)
|
||||||
.get('/posts?_sort=id&_sortDir=DESC')
|
.get('/posts?_sort=id&_sortDir=DESC')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(low.db.posts.reverse())
|
.expect(db.posts.reverse())
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -122,9 +126,9 @@ describe('Server', function() {
|
|||||||
request(server)
|
request(server)
|
||||||
.get('/comments?_start=1&_end=2')
|
.get('/comments?_start=1&_end=2')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect('x-total-count', low.db.comments.length.toString())
|
.expect('x-total-count', db.comments.length.toString())
|
||||||
.expect('Access-Control-Expose-Headers', 'X-Total-Count')
|
.expect('Access-Control-Expose-Headers', 'X-Total-Count')
|
||||||
.expect(low.db.comments.slice(1, 2))
|
.expect(db.comments.slice(1, 2))
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -135,8 +139,8 @@ describe('Server', function() {
|
|||||||
.get('/posts/1/comments')
|
.get('/posts/1/comments')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect([
|
.expect([
|
||||||
low.db.comments[0],
|
db.comments[0],
|
||||||
low.db.comments[1]
|
db.comments[1]
|
||||||
])
|
])
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
@ -147,7 +151,7 @@ describe('Server', function() {
|
|||||||
request(server)
|
request(server)
|
||||||
.get('/posts/1')
|
.get('/posts/1')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(low.db.posts[0])
|
.expect(db.posts[0])
|
||||||
.expect(200, done)
|
.expect(200, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -171,7 +175,7 @@ describe('Server', function() {
|
|||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res){
|
.end(function(err, res){
|
||||||
if (err) return done(err)
|
if (err) return done(err)
|
||||||
assert.equal(low.db.posts.length, 3)
|
assert.equal(db.posts.length, 3)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -188,7 +192,7 @@ describe('Server', function() {
|
|||||||
.end(function(err, res){
|
.end(function(err, res){
|
||||||
if (err) return done(err)
|
if (err) return done(err)
|
||||||
// assert it was created in database too
|
// assert it was created in database too
|
||||||
assert.deepEqual(low.db.posts[0], {id: 1, body: 'bar', booleanValue: true, integerValue: 1})
|
assert.deepEqual(db.posts[0], {id: 1, body: 'bar', booleanValue: true, integerValue: 1})
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -214,7 +218,7 @@ describe('Server', function() {
|
|||||||
.end(function(err, res){
|
.end(function(err, res){
|
||||||
if (err) return done(err)
|
if (err) return done(err)
|
||||||
// assert it was created in database too
|
// assert it was created in database too
|
||||||
assert.deepEqual(low.db.posts[0], {id: 1, body: 'bar'})
|
assert.deepEqual(db.posts[0], {id: 1, body: 'bar'})
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -236,8 +240,8 @@ describe('Server', function() {
|
|||||||
.expect(204)
|
.expect(204)
|
||||||
.end(function(err, res){
|
.end(function(err, res){
|
||||||
if (err) return done(err)
|
if (err) return done(err)
|
||||||
assert.equal(low.db.posts.length, 1)
|
assert.equal(db.posts.length, 1)
|
||||||
assert.equal(low.db.comments.length, 3)
|
assert.equal(db.comments.length, 3)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
@ -2,11 +2,11 @@ var assert = require('assert')
|
|||||||
var utils = require('../src/utils')
|
var utils = require('../src/utils')
|
||||||
|
|
||||||
describe('utils', function() {
|
describe('utils', function() {
|
||||||
|
|
||||||
describe('getRemovable', function() {
|
describe('getRemovable', function() {
|
||||||
|
|
||||||
it('should return removable documents', function() {
|
it('should return removable documents', function() {
|
||||||
|
|
||||||
var db = {
|
var db = {
|
||||||
posts: [
|
posts: [
|
||||||
{id: 1, comment: 1}
|
{id: 1, comment: 1}
|
||||||
@ -20,8 +20,8 @@ describe('utils', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var expected = [
|
var expected = [
|
||||||
['comments', 2],
|
{ name: 'comments', id: 2 },
|
||||||
['comments', 3]
|
{ name: 'comments', id: 3 }
|
||||||
]
|
]
|
||||||
|
|
||||||
assert.deepEqual(expected, utils.getRemovable(db))
|
assert.deepEqual(expected, utils.getRemovable(db))
|
||||||
@ -39,7 +39,7 @@ describe('utils', function() {
|
|||||||
assert.strictEqual(utils.toNative('string'), 'string')
|
assert.strictEqual(utils.toNative('string'), 'string')
|
||||||
assert.strictEqual(utils.toNative(1), 1)
|
assert.strictEqual(utils.toNative(1), 1)
|
||||||
assert.strictEqual(utils.toNative(true), true)
|
assert.strictEqual(utils.toNative(true), true)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user