diff --git a/README.md b/README.md index 6af2a25..e2a8cb5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Created with <3 for front-end developers who need a quick back-end for prototypi * [Egghead.io free video tutorial - Creating demo APIs with json-server](https://egghead.io/lessons/nodejs-creating-demo-apis-with-json-server) * [JSONPlaceholder - Live running version](http://jsonplaceholder.typicode.com) - + _See also [hotel](https://github.com/typicode/hotel), a simple process manager for developers._ ## Example @@ -70,6 +70,7 @@ To slice resources, add `_start` and `_end` or `_limit` (an `X-Total-Count` head ``` GET /posts?_start=20&_end=30 GET /posts/1/comments?_start=20&_end=30 +GET /posts/1/comments?_start=20&_limit=10 ``` To sort resources, add `_sort` and `_order` (ascending order by default). @@ -85,12 +86,18 @@ To make a full-text search on resources, add `q`. GET /posts?q=internet ``` -To embed other resources, add `_embed`. +To embed resources, add `_embed`. ``` GET /posts/1?_embed=comments ``` +To expand inner resources, add `_expand`. + +``` +GET /comments/1?_expand=post +``` + Returns database. ``` @@ -205,7 +212,7 @@ To modify responses, use `router.render()`: router.render = function (req, res) { res.jsonp({ body: res.locals.data - }) + }) } ``` diff --git a/src/server/router/plural.js b/src/server/router/plural.js index 23cd28a..8d2e054 100644 --- a/src/server/router/plural.js +++ b/src/server/router/plural.js @@ -8,11 +8,11 @@ module.exports = function (db, name) { // Create router var router = express.Router() - // GET /:resource - // GET /:resource?q= - // GET /:resource?attr=&attr= - // GET /:resource?_end=&* - // GET /:resource?_start=&_end=&* + // GET /name + // GET /name?q= + // GET /name?attr=&attr= + // GET /name?_end=&* + // GET /name?_start=&_end=&* function list (req, res, next) { // Filters list @@ -103,33 +103,51 @@ module.exports = function (db, name) { next() } - // GET /:resource/:id + // GET /name/:id + // GET /name/:id?_embed=&_expand function show (req, res, next) { var _embed = req.query._embed + var _expand = req.query._expand var id = utils.toNative(req.params.id) - var resource = db(name) - .getById(id) + var resource = db(name).getById(id) + + // Filter empty params + function filter (p) { + return p && p.trim().length > 0 + } if (resource) { // Clone resource to avoid making changes to the underlying object resource = _.cloneDeep(resource) + // Always use an array - _embed = _.isArray(_embed) ? _embed : [_embed] + _embed = [].concat(_embed) + _expand = [].concat(_expand) // Embed other resources based on resource id - _embed.forEach(function (otherResource) { + // /posts/1?_embed=comments + _embed + .filter(filter) + .forEach(function (otherResource) { + if (db.object[otherResource]) { + var query = {} + var singularResource = pluralize.singular(name) + query[singularResource + 'Id'] = id + resource[otherResource] = db(otherResource).where(query) + } + }) - if (otherResource - && otherResource.trim().length > 0 - && db.object[otherResource]) { - - var query = {} - var prop = pluralize.singular(name) + 'Id' - query[prop] = id - resource[otherResource] = db(otherResource).where(query) - - } - }) + // Expand inner resources based on id + // /posts/1?_expand=user + _expand + .filter(filter) + .forEach(function (innerResource) { + var plural = pluralize(innerResource) + if (db.object[plural]) { + var prop = innerResource + 'Id' + resource[innerResource] = db(plural).getById(resource[prop]) + } + }) res.locals.data = resource } @@ -137,7 +155,7 @@ module.exports = function (db, name) { next() } - // POST /:resource + // POST /name function create (req, res, next) { for (var key in req.body) { req.body[key] = utils.toNative(req.body[key]) @@ -151,8 +169,8 @@ module.exports = function (db, name) { next() } - // PUT /:resource/:id - // PATCH /:resource/:id + // PUT /name/:id + // PATCH /name/:id function update (req, res, next) { for (var key in req.body) { req.body[key] = utils.toNative(req.body[key]) @@ -168,7 +186,7 @@ module.exports = function (db, name) { next() } - // DELETE /:resource/:id + // DELETE /name/:id function destroy (req, res, next) { db(name).removeById(utils.toNative(req.params.id)) diff --git a/test/server/plural.js b/test/server/plural.js index de48b4d..aa2ee12 100644 --- a/test/server/plural.js +++ b/test/server/plural.js @@ -24,12 +24,17 @@ describe('Server', function () { {id: 3, body: 'photo'} ] + db.users = [ + {id: 1, username: 'Jim'}, + {id: 2, username: 'George'} + ] + db.comments = [ - {id: 1, body: 'foo', published: true, postId: 1}, - {id: 2, body: 'bar', published: false, postId: 1}, - {id: 3, body: 'baz', published: false, postId: 2}, - {id: 4, body: 'qux', published: true, postId: 2}, - {id: 5, body: 'quux', published: false, postId: 2} + {id: 1, body: 'foo', published: true, postId: 1, userId: 1}, + {id: 2, body: 'bar', published: false, postId: 1, userId: 2}, + {id: 3, body: 'baz', published: false, postId: 2, userId: 1}, + {id: 4, body: 'qux', published: true, postId: 2, userId: 2}, + {id: 5, body: 'quux', published: false, postId: 2, userId: 1} ] db.refs = [ @@ -258,6 +263,31 @@ describe('Server', function () { }) }) + describe('GET /:resource/:id?_expand=', function () { + it('should respond with corresponding resource and expanded inner resources', function (done) { + var comments = db.comments[0] + comments.post = db.posts[0] + request(server) + .get('/comments/1?_expand=post') + .expect('Content-Type', /json/) + .expect(comments) + .expect(200, done) + }) + }) + + describe('GET /:resource/:id?_expand=&_expand=', function () { + it('should respond with corresponding resource and expanded inner resources', function (done) { + var comments = db.comments[0] + comments.post = db.posts[0] + comments.user = db.users[0] + request(server) + .get('/comments/1?_expand=post&_expand=user') + .expect('Content-Type', /json/) + .expect(comments) + .expect(200, done) + }) + }) + describe('POST /:resource', function () { it('should respond with json, create a resource and increment id', function (done) {