Reputation: 1190
Strongloop api has been great to work with right out of the box. However I am currently trying to make some customizations. I am trying to add to the category
model a remote method that returns along with the endpoint data the following: perPage
, total
, paging
(shown below in more details). For paging
the before
and after
are parameters based on gameId
. limit
will default to 5
if value is null
or greater than 5
. What would be the best way to append to result the previously mentioned?
For example, http://localhost:3000/api/Categories/1004/games/mature?before=1000053
will return all gameId
less than 1000053
and before
all gameIds
greater than 1000053
.
common/models/category.js
Category.findById(id, {}, function(err, category){
if (err) return callback(err);
//set limit
if (limit && limit > 5){
limit = 5;
}else if(limit === undefined){
limit = 5;
}
//set after cursor
Games.find({
"where": {
categoryId: id,
mature: true,
gameId: {gt: after}
},
"limit": limit
}, function(err, gameArr) {
if (err) return callback(err);
callback(null, gameArr);
});
});
Endpoint results for /Categories/1004/games/mature
{
“perPage”: 5, //value from limit
“total”: 5, //total of return items from array
"data": [
... Endpoint data is here
],
"paging": {
"cursors": {
"after": 1000057, //last item in array
"before": 1000053 //first item in array
},
"previous": "http://localhost:3000/api/Categories/1004/games/mature?before=1000053" //url for previous
"next": "http://localhost:3000/api/Categories/1004/games/mature?after=1000057" //url for after
}
}
Upvotes: 1
Views: 1877
Reputation: 3396
To add results to the response, first change the type of response that the Category.mature remote method returns to an object:
Category.remoteMethod('mature', {
accepts: [
{arg: 'id', type: 'number', required: true},
{arg: 'limit',type: 'number',required: false},
{arg: 'after',type: 'number',required: false},
{arg: 'before',type: 'number',required: false}
],
// mixing ':id' into the rest url allows $owner to be determined and used for access control
http: {
path: '/:id/games/mature',
verb: 'get'
},
returns: {
arg: 'games',
type: 'object' // will be whatever you pass back as 2nd arg to callback()
}
});
Then just add the values you want to the new games object, using your existing gamesArr as the value for data
, and the values passed in for the limit, before, and after values into the response object as the 2nd argument to the success callback:
callback(null, {
"perPage": limit,
"total": gameArray.length,
"data": gameArray,
"paging": {
"cursors": {
"after": gameArray[gameArray.length-1].game_id, // last game_id in result
"before": gameArray[0].game_id // first game_id in result
},
"previous": "http://localhost:3000/api/Categories/1004/games/mature?before=" + gameArray[0].game_id,
"next": "http://localhost:3000/api/Categories/1004/games/mature?after=" + gameArray[gameArray.length-1].game_id
}
})
As I mentioned about the multiple Games.find() calls that were overcomplicating the code, you also shouldn't plunk down 3 or 4 copies of that object either. You can wrap the construction of the object in logic, then have a single callback() call to simplify the remote method's I/O management and write less code. Reducing to a single Games.find() call will make sending this result easier.
Also keep an eye out for curly apostrophe characters and tabs vs space indentation (pick one and stick with it, don't mix), it's much easier to help out when your code is constructed and organized with care.
Upvotes: 2
Reputation: 3396
The issue here is that you are using the Category model as the entry point, when it seems like you should be using the built-in query methods of the Game model with a filter and limit and offset, full stop. That will allow you to paginate without needing to add all this complexity to support your before/after concept. It's unnecessary. If your relations are set up correctly, all you need to paginate on games is a Games model query!
For example, it looks like you need a paginated list of games that are mature within a specific category, right? To get a result of 5 games with these criteria, you'd send a json REST API call like this:
http://0.0.0.0:3000/api/Games?filter=%7B%20%22where%22%3A%20%7B%22mature%22%3Atrue%2C%20%22categoryId%22%3A%201004%7D%2C%20%22offset%22%3A%200%2C%20%22limit%22%3A%205%20%7D
That's hard to read because it's encoded, but the underlying filter is constructed like this:
{ "where": {"mature":true, "categoryId": 1004}, "offset": 0, "limit": 5 }
This will return the following dataset, which is the first 5 games that are in the 1004 categoryId and also mature:
[
{
"mature": true,
"gameName": "CODBlacOps3",
"categoryId": 1004,
"gameId": 1000053,
"description": "Published by Activision",
"publishedDate": "2015-01-01T00:00:00.000Z"
},
{
"mature": true,
"gameName": "Evolve",
"categoryId": 1004,
"gameId": 1000054,
"description": "Published by Turtle Rock Studios",
"publishedDate": "2015-01-01T00:00:00.000Z"
},
{
"mature": true,
"gameName": "Battlefield4",
"categoryId": 1004,
"gameId": 1000055,
"description": "Published by EA Digital Illusions",
"publishedDate": "2013-01-01T00:00:00.000Z"
},
{
"mature": true,
"gameName": "Rainbow6",
"categoryId": 1004,
"gameId": 1000056,
"description": "Published by EUbisoft",
"publishedDate": "2015-01-01T00:00:00.000Z"
},
{
"mature": true,
"gameName": "Destiny",
"categoryId": 1004,
"gameId": 1000057,
"description": "Published by Bungie",
"publishedDate": "2014-01-01T00:00:00.000Z"
}
]
To get the next page of 5 games, we would just change the offset to 5, which tells the database to skip the first 5 because we're now on the 2nd page:
{ "where": {"mature":true, "categoryId": 1004}, "offset": 5, "limit": 5 }
This returns the following result:
[
{
"mature": true,
"gameName": "Wolfenstein",
"categoryId": 1004,
"gameId": 1000058,
"description": "Published by Bethesda",
"publishedDate": "2014-01-01T00:00:00.000Z"
},
{
"mature": true,
"gameName": "StarWarsBattleFront",
"categoryId": 1004,
"gameId": 1000059,
"description": "Published by EA DICE",
"publishedDate": "2015-01-01T00:00:00.000Z"
},
{
"mature": true,
"gameName": "Test1",
"categoryId": 1004,
"gameId": 1000060,
"description": "Published by Test1",
"publishedDate": "2015-12-19T00:00:00.000Z"
},
{
"mature": true,
"gameName": "Test2",
"categoryId": 1004,
"gameId": 1000061,
"description": "Published by Test2",
"publishedDate": "2015-12-19T00:00:00.000Z"
},
{
"mature": true,
"gameName": "Test3",
"categoryId": 1004,
"gameId": 1000062,
"description": "Published by Test3",
"publishedDate": "2015-12-19T00:00:00.000Z"
}
]
The Games are in the order of their game_id by default. No custom remote methods and nested find()s necessary!
If you wanted to order the list alphabetically, you could also add the order
filter, and set it to "order": "gameName ASC"
:
{ "where": {"mature":true, "categoryId": 1004}, "offset": 0, "limit": 5, "order": "gameName ASC" }
Which will return a new (page 1 because I set offset back to 0) array of games, ordered alphabetically by gameName
:
[
{
"mature": true,
"gameName": "Battlefield4",
"categoryId": 1004,
"gameId": 1000055,
"description": "Published by EA Digital Illusions",
"publishedDate": "2013-01-01T00:00:00.000Z"
},
{
"mature": true,
"gameName": "CODBlacOps3",
"categoryId": 1004,
"gameId": 1000053,
"description": "Published by Activision",
"publishedDate": "2015-01-01T00:00:00.000Z"
},
{
"mature": true,
"gameName": "Destiny",
"categoryId": 1004,
"gameId": 1000057,
"description": "Published by Bungie",
"publishedDate": "2014-01-01T00:00:00.000Z"
},
{
"mature": true,
"gameName": "Evolve",
"categoryId": 1004,
"gameId": 1000054,
"description": "Published by Turtle Rock Studios",
"publishedDate": "2015-01-01T00:00:00.000Z"
},
{
"mature": true,
"gameName": "Rainbow6",
"categoryId": 1004,
"gameId": 1000056,
"description": "Published by EUbisoft",
"publishedDate": "2015-01-01T00:00:00.000Z"
}
]
The way I was able to get this result so quickly was by using the API Explorer. I forked your project, changed the DB connection values to match my local server, started it up and then went to http://0.0.0.0/explorer/#!/Games/Games_find
and then played with different filter
values on the Games model:
Upvotes: 2