How do i document optional RESTful JSON API attributes? - json

I've been trying to figure out how to design the documentation for an API I'm building. I've been using Swagger (swagger.io) along with JSONSchema to help me structure the documentation, but I've run into a snag. Some of our object will have lots of what we call "metadata" attached to them, which is basically an arbitrary and variable dictionary of key/values. For example:
{
"id": "aabbbccdd",
"name": "Object 1",
"metadata": {
"attributeA": "This is a text attribute",
"attributeB": {
"key1": "complex attribute",
"key2": "complex attribute 2",
},
"attributeC": 1234
}
},
{
"id": "eeffffggghh",
"name": "Object 2",
"metadata": {
"attributeA": "This is a text attribute with a different value",
"attributeD": "Another text attribute",
"attributeE": True
}
}
So I could document the object representation by enumerating all the metadata attributes, but the list is long and will likely vary over time.
Is there another way to approach documenting this, or designing the API in a different way?

Related

Best Practice Design typed JSON Objects

Currently we are designing a System that will send JSON Objects to registered consumers. The consumers will be able to register for different Object Types that they want to receive. Currently we are designing the JSON Objects that will be consumed by the registered consumers.
As we see there are two possible approaches to define typed JSON Objects.
A generic JSON Object that has some properties set or it has them not set:
{
"timestamp": 1589448935,
"customer_id": "123123123123",
"message": {
"title": "Desired Title",
"text": "The Text of the Message",
}
}
{
"timestamp": 1589448935,
"customer_id": "123123123123",
"message": {
"title": "Desired Title",
"text": "The Text of the Message",
"avatar": "http://avatar.io/avatar"
}
}
A Type field on each JSON Object that specifies which Type this object is from:
{
"timestamp": 1589448935,
"customer_id": "123123123123",
"type": "simple_message",
"message": {
"title": "Title",
"text": "Message",
}
}
{
"timestamp": 1589448935,
"customer_id": "123123123123",
"type": "avatar_message",
"message": {
"title": "Title",
"text": "Message",
"avatar": "http://www.avatar.io/avatar"
}
}
From our point of view a more generic approach would be easier to handle within our system, because we would not need to handle multiple types, we just can append a property or leave it away.
From point of view of a developer I could imagine that a type field could help the developer when handling such objects (f.e. mapping them to objects) or switch on the type field to execute some specific logic.
Finally, to my question - which style is the one to be preferred (best-practice) when we want to make the life for consumers as easy as possible and why? Is there a best-practice approach for typed json objects?
If you are set on a generic application/json response then I would go with your option #2.
That "type" doesn't hurt and can only help clarify what that response is.

Is returning only IDs for a JSON API collection allowed?

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"
}
}
]
}

How to do deep sets and gets in Go's map[string]interface{}?

If I have some arbitrary JSON how can I do deep sets and gets on the nested properties using a slice of map keys and/or slice indexes?
For example, in the following excerpt from the JSON API example:
{
"data": [{
"type": "posts",
"id": "1",
"title": "JSON API paints my bikeshed!",
"links": {
"self": "http://example.com/posts/1",
"author": {
"self": "http://example.com/posts/1/links/author",
"related": "http://example.com/posts/1/author",
"linkage": { "type": "people", "id": "9" }
}
}
}]
}
I'd like to get the string "9" located at data.0.links.author.linkage.id using something like:
[]interface{}{"data",0,"links","author","linkage","id"}
I know the ideal way to do this is to create nested structs that map to the JSON object which I do for production code, but sometimes I need to do some quick testing which would be nice to do in Go as well.
You have stretchr/objx that provide a similar approach.
Example use:
document, _ := objx.FromJSON(json)
document.Get("path.to.field[0].you.want").Str()
However, unless you really don't know at all the structure of your JSON input ahead of time, this isn't the preferred way to go in golang…

Json Data Decoding without Library

I don't want to use a library. I want to figure out how to parse JSON data properly myself.
Example content :
If I was to parse this :
{"Name": [
{
"Type": "Type1",
"Content": "Content 1"
},
{
"Type": "Type1",
"Content": "Content 2"
},
{
"Type": "Type2",
"Content": "Content 3"
},
{
"Type": "Type2",
"Content": "Content 4"
}
]
}
Would I simply go about using indices and substrings and so on?
Or is there something about String manipulation that I am missing out on?
In javascript, eval() evaluates the expression. JSON is just a JS expression so it evaluates to an object. This is assuming the input is a valid JSON string. eval() runs all types of javascript code so beware of security.

How should a JSON response be formatted?

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.