diff --git a/src/index.js b/src/index.js index 3ac6e68..9e79b50 100644 --- a/src/index.js +++ b/src/index.js @@ -1,82 +1,4 @@ -var fs = require('fs') -var path = require('path') - -// 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 -} +module.exports = { + create: require('./server'), + router: require('./router') +} \ No newline at end of file diff --git a/src/router.js b/src/router.js new file mode 100644 index 0000000..23decee --- /dev/null +++ b/src/router.js @@ -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 +} diff --git a/src/routes.js b/src/routes.js deleted file mode 100644 index 89bb777..0000000 --- a/src/routes.js +++ /dev/null @@ -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() - } - } -} diff --git a/src/server.js b/src/server.js new file mode 100644 index 0000000..a7fd517 --- /dev/null +++ b/src/server.js @@ -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 +} \ No newline at end of file diff --git a/test/index.js b/test/index.js index 9651bf3..ce6b882 100644 --- a/test/index.js +++ b/test/index.js @@ -5,6 +5,7 @@ var jsonServer = require('../src/') describe('Server', function() { var server + var router var db beforeEach(function() { @@ -33,7 +34,8 @@ describe('Server', function() { {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() { @@ -290,7 +292,7 @@ describe('Server', function() { describe('Database #object', function() { it('should be accessible', function() { - assert(server.db.object) + assert(router.db.object) }) }) })