mirror of
https://github.com/typicode/json-server.git
synced 2025-07-28 12:43:18 +08:00
Separate routes and server creation
This commit is contained in:
86
src/index.js
86
src/index.js
@ -1,82 +1,4 @@
|
|||||||
var fs = require('fs')
|
module.exports = {
|
||||||
var path = require('path')
|
create: require('./server'),
|
||||||
|
router: require('./router')
|
||||||
// LowDB
|
}
|
||||||
var low = require('lowdb')
|
|
||||||
low.mixin(require('underscore-db'))
|
|
||||||
low.mixin(require('underscore.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 getRoutes = require('./routes')
|
|
||||||
|
|
||||||
low.mixin({ createId: utils.createId })
|
|
||||||
|
|
||||||
module.exports = function(object, filename) {
|
|
||||||
var server = express()
|
|
||||||
|
|
||||||
// Create database
|
|
||||||
if (filename) {
|
|
||||||
var db = low(filename)
|
|
||||||
} else {
|
|
||||||
var db = low()
|
|
||||||
db.object = object
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expose db
|
|
||||||
server.db = db
|
|
||||||
|
|
||||||
// Get routes
|
|
||||||
var routes = getRoutes(db)
|
|
||||||
|
|
||||||
// Don't use logger if json-server is mounted
|
|
||||||
if (!module.parent) {
|
|
||||||
server.use(logger('dev'))
|
|
||||||
}
|
|
||||||
|
|
||||||
server.set('json spaces', 2)
|
|
||||||
server.use(bodyParser.json({limit: '10mb'}))
|
|
||||||
server.use(bodyParser.urlencoded({ extended: false }))
|
|
||||||
server.use(methodOverride())
|
|
||||||
|
|
||||||
// Serve static files
|
|
||||||
if (fs.existsSync(process.cwd() + '/public')) {
|
|
||||||
server.use(serveStatic(process.cwd() + '/public'));
|
|
||||||
} else {
|
|
||||||
server.use(serveStatic(__dirname + '/public'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// CORS
|
|
||||||
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
|
|
||||||
}
|
|
190
src/router.js
Normal file
190
src/router.js
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
var express = require('express')
|
||||||
|
var _ = require('underscore')
|
||||||
|
var utils = require('./utils')
|
||||||
|
var low = require('lowdb')
|
||||||
|
|
||||||
|
low.mixin(require('underscore-db'))
|
||||||
|
low.mixin(require('underscore.inflections'))
|
||||||
|
low.mixin({ createId: utils.createId })
|
||||||
|
|
||||||
|
module.exports = function(source) {
|
||||||
|
// Create router
|
||||||
|
var router = express.Router()
|
||||||
|
|
||||||
|
// Create database
|
||||||
|
if (_.isObject(source)) {
|
||||||
|
var db = low()
|
||||||
|
db.object = source
|
||||||
|
} else {
|
||||||
|
var db = low(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose database
|
||||||
|
router.db = db
|
||||||
|
|
||||||
|
// GET /db
|
||||||
|
function showDatabase(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=
|
||||||
|
function list(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 _order = req.query._order
|
||||||
|
|
||||||
|
delete req.query._start
|
||||||
|
delete req.query._end
|
||||||
|
delete req.query._sort
|
||||||
|
delete req.query._order
|
||||||
|
|
||||||
|
if (req.query.q) {
|
||||||
|
|
||||||
|
// Full-text search
|
||||||
|
var q = req.query.q.toLowerCase()
|
||||||
|
|
||||||
|
array = db(req.params.resource).filter(function(obj) {
|
||||||
|
for (var key in obj) {
|
||||||
|
var value = obj[key]
|
||||||
|
if (_.isString(value) && value.toLowerCase().indexOf(q) !== -1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
} 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) {
|
||||||
|
// don't take into account JSONP query parameters
|
||||||
|
// jQuery adds a '_' query parameter too
|
||||||
|
if (key !== 'callback' && key !== '_') {
|
||||||
|
filters[key] = utils.toNative(req.query[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter
|
||||||
|
if (_(filters).isEmpty()) {
|
||||||
|
array = db(req.params.resource).value()
|
||||||
|
} else {
|
||||||
|
array = db(req.params.resource).filter(filters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
if(_sort) {
|
||||||
|
_order = _order || 'ASC'
|
||||||
|
|
||||||
|
array = _.sortBy(array, function(element) {
|
||||||
|
return element[_sort];
|
||||||
|
})
|
||||||
|
|
||||||
|
if (_order === '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
|
||||||
|
function show(req, res, next) {
|
||||||
|
var resource = db(req.params.resource)
|
||||||
|
.get(+req.params.id)
|
||||||
|
|
||||||
|
if (resource) {
|
||||||
|
res.jsonp(resource)
|
||||||
|
} else {
|
||||||
|
res.status(404).jsonp({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /:resource
|
||||||
|
function create(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)
|
||||||
|
|
||||||
|
res.jsonp(resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT /:resource/:id
|
||||||
|
// PATCH /:resource/:id
|
||||||
|
function update(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)
|
||||||
|
|
||||||
|
if (resource) {
|
||||||
|
res.jsonp(resource)
|
||||||
|
} else {
|
||||||
|
res.status(404).jsonp({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /:resource/:id
|
||||||
|
function destroy(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()
|
||||||
|
}
|
||||||
|
|
||||||
|
router.get('/db', showDatabase)
|
||||||
|
|
||||||
|
router.route('/:resource')
|
||||||
|
.get(list)
|
||||||
|
.post(create)
|
||||||
|
|
||||||
|
router.route('/:resource/:id')
|
||||||
|
.get(show)
|
||||||
|
.put(update)
|
||||||
|
.patch(update)
|
||||||
|
.delete(destroy)
|
||||||
|
|
||||||
|
router.get('/:parent/:parentId/:resource', list)
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
156
src/routes.js
156
src/routes.js
@ -1,156 +0,0 @@
|
|||||||
var _ = require('underscore')
|
|
||||||
var utils = require('./utils')
|
|
||||||
|
|
||||||
module.exports = function(db) {
|
|
||||||
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 _order = req.query._order
|
|
||||||
|
|
||||||
delete req.query._start
|
|
||||||
delete req.query._end
|
|
||||||
delete req.query._sort
|
|
||||||
delete req.query._order
|
|
||||||
|
|
||||||
if (req.query.q) {
|
|
||||||
|
|
||||||
// Full-text search
|
|
||||||
var q = req.query.q.toLowerCase()
|
|
||||||
|
|
||||||
array = db(req.params.resource).filter(function(obj) {
|
|
||||||
for (var key in obj) {
|
|
||||||
var value = obj[key]
|
|
||||||
if (_.isString(value) && value.toLowerCase().indexOf(q) !== -1) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
} 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) {
|
|
||||||
// don't take into account JSONP query parameters
|
|
||||||
// jQuery adds a '_' query parameter too
|
|
||||||
if (key !== 'callback' && key !== '_') {
|
|
||||||
filters[key] = utils.toNative(req.query[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter
|
|
||||||
if (_(filters).isEmpty()) {
|
|
||||||
array = db(req.params.resource).value()
|
|
||||||
} else {
|
|
||||||
array = db(req.params.resource).filter(filters)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort
|
|
||||||
if(_sort) {
|
|
||||||
_order = _order || 'ASC'
|
|
||||||
|
|
||||||
array = _.sortBy(array, function(element) {
|
|
||||||
return element[_sort];
|
|
||||||
})
|
|
||||||
|
|
||||||
if (_order === '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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
40
src/server.js
Normal file
40
src/server.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
var fs = require('fs')
|
||||||
|
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')
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
var server = express()
|
||||||
|
|
||||||
|
// Don't use logger if json-server is mounted
|
||||||
|
if (!module.parent) {
|
||||||
|
server.use(logger('dev'))
|
||||||
|
}
|
||||||
|
|
||||||
|
server.set('json spaces', 2)
|
||||||
|
server.use(bodyParser.json({limit: '10mb'}))
|
||||||
|
server.use(bodyParser.urlencoded({ extended: false }))
|
||||||
|
server.use(methodOverride())
|
||||||
|
|
||||||
|
// Serve static files
|
||||||
|
if (fs.existsSync(process.cwd() + '/public')) {
|
||||||
|
server.use(serveStatic(process.cwd() + '/public'));
|
||||||
|
} else {
|
||||||
|
server.use(serveStatic(__dirname + '/public'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// CORS
|
||||||
|
server.use(cors({ origin: true, credentials: true }))
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
// only use in development
|
||||||
|
server.use(errorhandler())
|
||||||
|
}
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
@ -5,6 +5,7 @@ var jsonServer = require('../src/')
|
|||||||
describe('Server', function() {
|
describe('Server', function() {
|
||||||
|
|
||||||
var server
|
var server
|
||||||
|
var router
|
||||||
var db
|
var db
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
@ -33,7 +34,8 @@ describe('Server', function() {
|
|||||||
{id: 'abcd-1234', url: 'http://example.com', postId: 1}
|
{id: 'abcd-1234', url: 'http://example.com', postId: 1}
|
||||||
]
|
]
|
||||||
|
|
||||||
server = jsonServer(db)
|
router = jsonServer.router(db)
|
||||||
|
server = jsonServer.create().use(router)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('GET /db', function() {
|
describe('GET /db', function() {
|
||||||
@ -290,7 +292,7 @@ describe('Server', function() {
|
|||||||
|
|
||||||
describe('Database #object', function() {
|
describe('Database #object', function() {
|
||||||
it('should be accessible', function() {
|
it('should be accessible', function() {
|
||||||
assert(server.db.object)
|
assert(router.db.object)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user