I am designing a JSON API for a mobile app and have to decide how to show child objects from the server to the client. Typically the request from the client to the server will be a single request to sync and the response will include all the objects that need updating. What is the best way to show the objects?
Option A - Nested Children:
{ "articles": [
{ "id" : 1,
"title": "This is the first article",
"comments": [
{"id": "1",
"article_id" : "1",
"title": "A comment on the first article"
}]
},
{ "id" : 2,
"title": "This is the second article",
"comments": [
{"id": "2",
"article_id" : "2",
"title": "A comment on the second article"
}]
}, ]}
OPTION B - All Objects on Their Own
{ "articles": [
{ "id" : 1,
"title": "This is the first article",
}
{ "id" : 2,
"title": "This is the second article",
}]
"comments": [
{"id": "1",
"article_id" : "1",
"title": "A comment on the first article"
},
{"id": "2",
"article_id" : "2",
"title": "A comment on the second article"
}]}
On the client side I can handle either format and build the relationship based on the article_id field so I am not too sure why nest the children, other than that it makes it look nice. However, when I think about writing tests for the client-side, especially the mapping of json to objects it seems easier to show and map each object on its own. I am a beginner here so any thoughts would be helpful.
PS. I am building the server using Rails/Grape and the clients with RestKit/Coredata (iOS) and probably RoboSpice/ORMLite (Android).
That's very subjective. There isn't one correct answer for that. It really depends on whatever approach is more suited to your task and data. You say this is a request used to sync data. How is the data represented and stored on the client side? If flat, like a relational database, then the flat output is probably easier to use. On the other hand, if the client will use the relationships a lot, it's probably better to use the nested structure.
From an API design standpoint, I'd have the endpoint for the articles collection accept a query parameter like expand, with a level number, or named entities, and it would add the nested children accordingly. So, for instance, GET /api/articles?expand=comments would generate output with nested comments, or GET /api/articles?expand=1 to generate output with all immediate children. That way, clients can easily generate the nested output if they need it, or they can query the endpoints for articles and comments separately and concatenate the output if they need the flat data.,
Related
I'm trying to load a json file from a server response and parsing it in flutter, the model i create is working for all the other fields but i'm in trouble with this class
this is a part of the JSON response:
"episodes": {
"1": [
{
"id": "63",
"episode_num": 1,
"title": "Some Name",
"container_extension": "mp4",
"info": {
"director": "",
"plot": "",
"cast": "",
"rating": "",
"releasedate": "",
"movie_image": "",
"genre": "",
"duration_secs": 6495,
"duration": "01:48:15"
}
}
]
}
in this case the entry under episodes is just one but this will represents a season and all the episode inside it, so under episodes many of this entry (undefined number during coding) can be present
At this time, using online json to dart converter tools i can be able to retrive just this one entry but if a response have more than 1 season i can't see it.
There is any way to handle this?
EDIT:
Solved using a for cicle with max value = (json['episodes'].length + 1).
For the info stored inside each 'episodes' value i can use
json['episodes']['$i']
Valid JSON is always convertible to a Dart data structure. But what you may be asking is "can I get nested objects from this?", and that just depends on how hard you want to work. Some JSON-to-Dart tools are better than others and some JSON values are impossible for any automated tool to make sense of. Only real answer is: "it depends".
So let's say I have a resources called articles. These have a numeric id and you can access them under something such as:
GET /articles/1 for a specific article.
And let's say that returns something like:
{
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON:API paints my bikeshed!",
"body": "A bunch of text here"
}
}
}
Now my question is how to handle a request to GET /articles. I.e. how to deal with the request to the collection.
You see, accessing the body of the article is slow and painful. The last thing I want this REST API to do is actually try to get all that information. Yet as far as I can tell the JSON API schema seems to assume that you can always return full resources.
Is there any "allowed" way to return just the IDs (or partial attributes, like "title") under JSON API while actively not providing the ability to get the full resource?
Something like:
GET /articles returning:
{
"data": [
{
"type": "article_snubs",
"id": 1,
"attributes": {
"title": "JSON:API paints my bikeshed!"
}
}, {
"type": "article_snubs",
"id": 2,
"attributes": {
"title": "Some second thing here"
}
}
]
}
Maybe with links to the full articles?
Basically, is this at all possible while following JSON API or a REST standard? Because there is absolutely no way that GET /articles is ever going to be returning full resources due to the associate cost of getting the data, which I do not think is a rare situation to be in.
As far as I understand the JSON API specification there is no requirement that an API must return all fields (attributes and relationships) of a resource by default. The only MUST statement regarding fields inclusion that I'm aware of is related to Sparse Fieldsets (fields query param):
Sparse Fieldsets
[...]
If a client requests a restricted set of fields for a given resource type, an endpoint MUST NOT include additional fields in resource objects of that type in its response.
https://jsonapi.org/format/#fetching-sparse-fieldsets
Even so this is not forbidden by spec I would not recommend that approach. Returning only a subset of fields makes consuming your API much harder as you have to consult the documentation in order to get a list of all supported fields. It's much more within the meaning of the spec to let the client decide which information (and related resources) should be included.
The "attributes" object of a JSON-API doc does not need to be a complete representation:
attributes: an attributes object representing some of the resource’s data.
You can provide a "self" link to get the full representation, or perhaps even a "body" link to get just the body:
links: a links object containing links related to the resource.
E.g.
{
"data": [
{
"type": "articles_snubs",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"links": {
"self": "/articles/1",
"body": "/articles/1/body"
}
},
{
"type": "article_snubs",
"id": "2",
"attributes": {
"title": "Some second thing here"
},
"links": {
"self": "/articles/2",
"body": "/articles/2/body"
}
}
]
}
I have this particular object which contains the my_array:
"description": "My Object Description",
"my_array": [
{
"id": 1000,
"name": "abc",
"url" : "abc.html",
"content": "somebig content"
},
{
"id": 1001,
"name": "def",
"url" : "def.html",
"content": "somebig content"
},
{
"id": 1002,
"name": "xyz",
"url" : "xyz.html",
"content": "somebig content"
} ]
Each element in array contains a url. Now whenever this object changes, i have a task which hits the url for each element of the array, gets the html content for that element, and creates request document which can be indexed into elasticsearch.
Lets say, the url for id = 1001 is not accessible, and content for this element cannot be accessed. I still want to go ahead and process changes for elements 1000, and 1002. In that case my update would look like this:
"description": "My New Object Description",
"my_array": [
{
"id": 1000,
"name": "abc",
"url" : "abc-new-url.html",
"content": "some modified content"
},
{
"id": 1002,
"name": "xyz",
"url" : "xyz-new-url.html",
"content": "some modified content"
} ]
If i send this partial update to elasticsearch, the collection gets updated but element 1001 is removed from the collection.
My problem is how can i selectively update elements 1000, and 1002 without touching 1001. Index being stale with 1001 here is ok for me. One obvious choice is to fetch the existing doc from elasticsearch, and do the merging manually before doing the update. Is there any other way this partial update can be performed?
Another question, is there any way to send just the url to elasticsearch, and write a plugin to fetch the html content at index time, rather then doing it beforehand?
I think you could solve this using scripting in a update query, see these answers here:
remove objects from array elastic search
You can't do such an update using Elasticsearch native APIs. However, if you don't want to merge the updated content manually on your application level, a possible solution is to store each element of the array in a document with the same index as your original document, but different type.
Then do the update for each one of these elements (which in this case becomes documents) separately
What is the right way to format your responses in JSON and why? I've seen different services do it two ways, consider a simple GET /users resource:
{
"success": true,
"message": "User created successfully",
"data": [
{"id": 1, "name": "John"},
{"id": 2, "name": "George"},
{"id": 3, "name": "Bob"},
{"id": 4, "name": "Jane"}
]
}
That is how I usually do that. I have some abstract helper fields like success and message, there may be some more but the question is if should I nest the data in the data field to an array called the same way as the resource - users:
{
"success": true,
"message": "User created successfully",
"data": {
"users": [
{"id": 1, "name": "John"},
{"id": 2, "name": "George"},
{"id": 3, "name": "Bob"},
{"id": 4, "name": "Jane"}
]
}
}
Even if we don't use the abstraction:
{
"users": [
{"id": 1, "name": "John"},
{"id": 2, "name": "George"},
{"id": 3, "name": "Bob"},
{"id": 4, "name": "Jane"}
]
}
Seems the users key is obsolete as any client will know the route they called, which consists of /users, where users are mentioned, and the client code like
$users = $request->perform('http://this.api/users')->body()->json_decode();
looks much better than
$users = $request->perform('http://this.api/users')->body()->json_decode()->users;
as it avoids repeated users.
One use case where the envelope can be useful is when you are expecting to be dealing with large lists and need to do pagination to prevent huge response payloads. The envelope is a good place to put the pagination meta data:
{
"users": [...],
"offset": 0,
"limit": 50,
"total": 10000
}
(This is what we do in a RESTful API I'm working on)
Clearly this is only relevant for requests that return lists of things (e.g. /users/) and not for requests that return single entities (e.g. /users/42) and even for requests that return lists, you don't have to use an envelope - one alternative would be to use response headers for this meta data instead.
PS. I would only advise having a success and message fields if you have a concrete use case for them. Otherwise don't bother, they are simply unnecessary.
Just to get on the same page, data is a field in a JSON object. In the first example the value of data is an array. In the second example the value of data is an object.
Either is valid, so to answer your question: no it is not necessary to nest named objects in an named object. It is necessary that all fields of an object be named, but you are free to nest arrays within an object.
It really just depends on what the processor expects. If data can be anything, then the first approach is fine. If code expects the value of the data field to be an object, then you have to use something like the second example.
According to your comment which you added to first comment: more descriptive data is better data as every information is useful for consumer of you API - REST endpoint. So if you know that the content is user, or whatever, it's better to use it in schema or endpoint url.
Better description = better consuption :-)
I have a REST service that returns a list of objects. Each object contains objectcode and objectname.
This is my first time building a REST service, so I'm not sure how to format the response.
Should it be:
{
"objects": {
"count": 2,
"object": [
{
"objectcode": "1",
"objectname": "foo"
},
{
"objectcode": "2",
"objectname": "bar"
},
...more objects
]
}
}
OR
[
{
"objectcode": "1",
"objectname": "foo"
},
{
"objectcode": "2",
"objectname": "bar"
},
...more objects
]
I realize this might be a little subjective, but which would be easier to consume? I would also need to support XML formatted response later.
They are the same to consume, as a library handles both just fine. The first one has an advantage over the second though: You will be able to expand the response to include other information additional to the objects (for example, categories) without breaking existing code.
Something like
{
"objects": {
"count": 2,
"object": [
{
"objectcode": "1",
"objectname": "foo"
},
{
"objectcode": "2",
"objectname": "bar"
},
...more objects
]
}
"categories": {
"count": 2,
"category" : [
{ "name": "some category"}
]
}
}
Additionally, the json shouldn't be formatted in any way, so remove whitespace, linebreaks etc. Also, the count isn't really necessary, as it will be saved while parsing the objects themselves.
I often see the first one. Sometimes it's easier to manipulate data to have meta-data. For exemple google API use first one : http://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=true
It's not only the question of personal preference; it's also the question fo your requirements. For example, if I was in the same situation and I did need object count on client side then I'd go with first approach otherwise I will choose the second one.
Also please note that "classic" REST server mostly will work a bit different way. If some REST function is to return a list of objects then it should return only a list of URLs to those objects. The URLs should be pointing to details endpoints - so by querying each endpoint you may get details on specific single object.
As a client I would prefer the second format. If the first format only includes the number of "objects", this is redundant information.