RESTful API cursor pagination with total count - json

I'm designing a RESTful API, returns result with pagination and total number.
Here is my approach
Endpoint
[GET] /items
Parameters
Parameter
Description
before_cursor
Return results before cursor
after_cursor
Return results after cursor
limit
Limit of result
Response
{
"items": [
...
],
"pagination": {
"prev_cursor": string, // cursor before the range, for backward navigation
"next_cursor": string, // cursor after the range, for forward navigation
"total": number, // total number of items
"total_before": number, // item count before prev_cursor helps client positioning
"total_after": number // item count before next_cursor helps client positioning
}
}
Example
Raw data
Item_0, Item_1, ... Item_99
Get first page
[GET] /items?limit=10
{
"items": [item_0, ..., item_9],
"pagination": {
"next_cursor": "item_10",
"total": 100,
"total_before": 0,
"total_after": 90
}
}
Navigate to next page
[GET] /items?limit=10&after_cursor=item_10
{
"items": [item_10, ..., item_19],
"pagination": {
"before_cursor": "item_9",
"next_cursor": "item_20",
"total": 100,
"total_before": 10,
"total_after": 80
}
}
Navigate back to previous page
[GET] /items?limit=10&before_cursor=item_9
{
"items": [item_0, ..., item_9],
"pagination": {
"next_cursor": "item_10",
"total": 100,
"total_before": 0,
"total_after": 90
}
}
Is it a good approach? Or is there a neater way to design API for this requirement?

There are multiple ways to design this endpoint with a rest approach. I found these 2 the best when you need to deal with collections.
Approach 1
GET /items?page=2 & page_size=10
This should return only 10 elements from your resource items and skip the first page (it is up to you to decide if you start your collection with a 0 or with 1)
A response should look like this
Response 1:
[
item1,
item2,
...
item10
]
Response Header: RecordCount or X-Total-Count = 530
The total count in this case is included in the headers.
Now if the user wants to paginate the next page or the previous they can do the following request:
next: GET /items?page=3 & page_size=10
previous: GET /items?page=1 & page_size=10
Including this request as part of the payload and trying to comply with Rest for HATEOAS is what I think can be the second approach
Response 2:
{
TotalCount=530,
Result:
[
item1,
item2,
...
item10
],
Next: '/items?page=3 & page_size=10'
Previous: 'GET /items?page=1 & page_size=10'
}
In this case, the total count is included in the response and the items on the result and you build the next and previous URLs

Related

How to handle two types of json responses?

The response of a request is different depending on how many cotas my client has.
Let's see some code:
//Validar Cliente
$clienteSiaconSOAP = new \SoapClient('http://xxxxxx/Services.asmx?WSDL');
$paramClienteSiacon = array(
'iDDD' => 0,
'iNumeroTelefone' => 0,
'iCPF_CNPJ' => $request->nroCpfCnpj,
);
$responseClienteSiacon = $clienteSiaconSOAP->ROS_ValidarCliente($paramClienteSiacon);
foreach ($responseClienteSiacon as $clienteSiacon) {
$cSiacon = json_decode(json_encode($clienteSiacon), true);
}
return $cSiacon['Cliente'];
die();
If the client does have one cota, the response is:
{"CODIGO-TIPO-PESSOA":"F","CPF-CNPJ":"*******","CODIGO-GRUPO":"04921","NUMERO-SEQUENCIA":0,"NUMERO-COTA":47,"NOME-CLIENTE":"SILVA SOUZA","NUMERO-CONTRATO":*****,"DESCRICAO-BEM":"Pacote de Servi\u00e7os 102","VALOR-BEM":11565,"NUMERO-TELEFONE":"034 888888888","DATA-PROXIMA-REUNIAO":"20150511","SITUACAO-COBRANCA":"Q","DESCRICAO-SITUACAO-COBRANCA":"Quita\u00e7\u00e3o","FASE-SITUACAO-COBRANCA":"Q000","CODIGO-PLANO-COTA":36,"DATA-ENTREGA":"20130624","DATA-CONTEMPLACAO":"20130613","PERC-TOTAL-PAGO":100,"PERC-TOTAL-PENDENTE":0,"PERC-QUITACAO":0,"CODIGO-FORMA-PAGAMENTO":1,"DATA-NASCIMENTO":"19661027","DATA-CANCELAMENTO":"","CADASTRO-ATUALIZADO":"S","SEGMENTO-CADOC":6,"CEP":38406392}
And if a client has more than one cota, the response is:
[
{
"CODIGO-TIPO-PESSOA": "J",
"CPF-CNPJ": "00635344000177",
"CODIGO-GRUPO": "07384",
"NUMERO-SEQUENCIA": 0,
"NUMERO-COTA": 853,
"NOME-CLIENTE": "AUTO ESCOLA GUILHERMITTI E L LTDA",
"NUMERO-CONTRATO": 859866,
"DESCRICAO-BEM": "HONDA NXR 160 BROS",
"VALOR-BEM": 12975,
"NUMERO-TELEFONE": "017 32581859",
"DATA-PROXIMA-REUNIAO": "20190322",
"SITUACAO-COBRANCA": "N",
"DESCRICAO-SITUACAO-COBRANCA": "Normal",
"FASE-SITUACAO-COBRANCA": "N000",
"CODIGO-PLANO-COTA": 31,
"DATA-ENTREGA": "20180507",
"DATA-CONTEMPLACAO": "20170622",
"PERC-TOTAL-PAGO": 87.7196,
"PERC-TOTAL-PENDENTE": 3.1401,
"PERC-QUITACAO": 12.2804,
"CODIGO-FORMA-PAGAMENTO": 1,
"DATA-NASCIMENTO": "",
"DATA-CANCELAMENTO": "",
"CADASTRO-ATUALIZADO": "N",
"SEGMENTO-CADOC": 4,
"CEP": 15115000
},
{... and 3 more ... ]
I am without ideas on how to deal with it. I have tried count(), sizeof() but the result on the first case is like 26 and at the second, 4 which makes more sense in this case, cause there are 4 cotas at the second scenario.
It is needed 'cause if the client has more than one cota, must show a list of it.
Any ideas, please?
As my old brother always says, "Code less, Produce more".
I used the strlen() to get the size of the response and it works like a breeze!
if (strlen($cSiacon) > 916) {
return 'É multi cota';
} else {
return 'É mono conta';
}
die();
As the minimum size is always 916, it is done!

DataTables footerCallback - conditional on another column value

I'm trying to implement a footerCallback in DataTables that do a conditional sum of some columns, based on a cell that's in a different column in the same row.Can anyone help me with this? I have used below code and check alert(cur_index); but I think it is not working as expected. And I did not get a correct sum of values of a column. My code is:
pageTotal6 = api
.column( 6, { page: 'current'} )
.data()
.reduce( function (a, b) {
var cur_index = api.column(6).data().indexOf(b);
alert(cur_index);
alert(api.column(3).data()[cur_index]);
if (api.column(3).data()[cur_index] != "Pending review") {
return parseInt(a) + parseInt(b);
}
else { return parseInt(a); }
return intVal(a) + intVal(b);
}, 0 );
And in 3rd column I have some repeated value and I want sum only for distinct value from 3rd column. How can I do this 2 thing using datatable & html
Theres two ways you can go about this.
First Method
(I will assume you are reading JSON data from Database [ViewModel] in C#, and using server-side processing)
Using the image below as reference to how I solved the problem
I wanted to sum of the "Amount" column where "Measure Type" (last column) != 99. First thing I did with the ViewModel that was going to pass the list to my JSON object was add a column sum column that didnt read any MeasureType = 99 rows from the table.
So essentially my JSON object has two columns that read the Amount column data, one is visible that you see in the image that has all figures and another invisible that only reads the values I want to sum in my footer.
while (MyDataReader.Read())
{
//get all other columns
//column with amount figures measuretype != 99
if (reportData.q_measuretype != 99)
{
reportData.amountNo99 = Convert.ToDecimal(String.Format("{0:0.00}", read["q_amount"]));
}
else
{
reportData.amountNo99 = 0;
}
list.Add(reportData);
}
After that step, then within the footerCallback function you can keep it simple by just summing the invisible column, because the condition has already been set when you get the list of rows onto the page
totalNettNo99 = api
.column(8, { page: 'current' }) //remember this is the last invisible column
.data()
.reduce(function (a, b) {
return intVal(a) + intVal(b);
});
You can then update your footer with that sum on the visible column 3 (index 2)
$(api.column(2).footer()).html(
'€' + totalNettNo99.toFixed(2)
);
Remember to set the invisble column this way in "columnDefs"
"ajax": {
"url": "/Reports/loadTransactionList",
"type": "POST",
"datatype": "JSON"
},
"columnDefs": [
{
"targets": [8],
"visible": false,
"searchable": false,
"render": false
}
],
"columns": [
{
"data": "convertDateToString"
},
{
"data": "convertTimeToString"
},
{
"data": "q_receiptnumber"
},
As you can see from the image, only the rows with Guinness Pint total the sum on the footer. Its a bit more typing but solves the problem if you have been tearing your hair with script solution.
Second Method
You can have a look at this answer here done purely in script and less typing that my solution
https://stackoverflow.com/a/42215009/7610106
credit to nkbved

Cursor-based Pagination in Facebook's Graph API

according to graphApi's documentation :
Cursor-based Pagination
Cursor-based pagination is the most efficient method of paging and should always be used where possible. A
cursor refers to a random string of characters which marks a specific
item in a list of data. Unless this item is deleted, the cursor will
always point to the same part of the list, but is be invalidated if an
item is removed. Therefore, your app shouldn't store any older cursors
or assume that they will still be valid.
When reading an edge that supports cursor pagination, you will see the
following JSON response:
{
"data": [
... Endpoint data is here
],
"paging": {
"cursors": {
"after": "MTAxNTExOTQ1MjAwNzI5NDE=",
"before": "NDMyNzQyODI3OTQw"
},
"previous": "https://graph.facebook.com/me/albums?limit=25&before=NDMyNzQyODI3OTQw"
"next": "https://graph.facebook.com/me/albums?limit=25&after=MTAxNTExOTQ1MjAwNzI5NDE="
}
}
but i have no idea what's going on up here, anyone can point me on how we do Cursor-based Pagination here? i've done similar thing by passing the Max_id but in here its not the case
this is how i'm making my first call
accessKey = "\(appID)|\(appSecret)"
let connection = GraphRequestConnection()
let request = GraphRequest.init(graphPath:"/myPageName/posts", parameters: [ "access_token" : accessKey, "fields" : "message,full_picture,created_time,story,type,permalink_url" , "limit": "10"], accessToken: nil, httpMethod: .GET, apiVersion: "2.8")
connection.add(request) { httpResponse, result in
switch result {
case .success(let response):
//FETCHED DATA HERE///
case .failed(let error):
print("Graph Request Failed: \(error)")
}
}
connection.start()
(Posted on behalf of the OP).
Passing offset in parameter did the job.
For this lets first understand Cursor paging with an example :
Let’s assume we want to paginate from the most recent user to the oldest user.When client request for the first time , suppose we select the first page through query:
SELECT * FROM users
WHERE team_id = %team_id
ORDER BY id DESC
LIMIT %limit
Where limit is equal to limit plus one, to fetch one more result than the count specified by the client. The extra result isn’t returned in the result set, but we use the ID of the value as the next_cursor.
The response from the server would be:
{
"users": [...],
"next_cursor": "1234", # the user id of the extra result
}
The client would then provide next_cursor as cursor in the second request.
SELECT * FROM users
WHERE team_id = %team_id
AND id <= %cursor
ORDER BY id DESC
LIMIT %limit
Coming back to Facebook Implementation : As the name suggests the next cursor will fetch you the next set of results and the previous cursor will fetch you the previous set of results.
If you still have problem understanding Cursors and why they are used you can check out this article. The above example is quoted from the same.

Best way to handle data list of REST web service with foreign key(one to many)

I am going to implement the REST base CRUD modal in my my app.I wan to display the list of product data with edit and delete link
Product
id, title, unit_id, product_type_id, currency_id,price
Q1: what should be json response look like?
There are two formats comes in my mind to place the data in Json as a response of REST Get call
[
{
id:1,
title:"T-Shirt",
unit_id:20,
unit_title: "abc"
product_type_id:30,
product_type_title:"xyz"
currency_id: 10,
currency_name: "USD"
min_price:20
},
{...}
]
and the another one is
[
{
id:1,
title:"T-Shirt",
unit: {
id: 20,
title: "abc"
},
product_type: {
id: 30,
title: "xyz"
},
currency_id: {
id:10,
name: "USD"
},
min_price:20
},
{...}
]
what is the better and standard way to handle the above scenario?
Furthermore, let suppose I have 10 more properties in product table which will never display on list page. but i needed it when user going to edit the specific item.
Q2: Should I the load all data once at the time of displaying product list and pass the data to edit component.
or
Load only the needed propeties of product table and pass the id to produt edit component and a new REST GET call with id to get the properties of product.
I am using React + Redux for my front end
Typically, you would create additional methods for API consumers to retrieve the values that populate the lists of currency, product_type and unit when editing in a UI.
I wouldn't return more data than necessary for an individual Product object.

Couchbase range query

i have testa couchbase 4 server, to store sensor measurement data.
I have following view:
function (doc, meta) {
if (doc.aks) {
emit([doc.aks, doc.timestamp], {
value: doc.value,
status: doc.status
});
}
}
Here an example document:
{
"timestamp": 1199180981,
"value": 0,
"status": 2147483650,
"aks": "BN028:H23:VS:001:Zustand"
}
I try now following query to this view:
Give me a timerange of values for a single sensor.
/_view/timeline?stale=false&startkey=["BN020:H03:ZW:102:MC_t_return",12675419334]&endkey=["BN020:H03:ZW:102:MC_t_return",13675419334]
But this will give no result.
I get an result if i jsut use startkey= or endkey= but not if i use both.
What do i wrong?
Make sure you actually have data in your bucket that would fall within your range. I used your View and your Query and received results. Your example document is not within the range, the aks is wrong and the timestamp is too early. If you add a document that is in the range you should receive it back. This one is the lower bound of your range:
{
"timestamp": 12675419334,
"value": 0,
"status": 2147483650,
"aks": "BN020:H03:ZW:102:MC_t_return"
}