Best practice for passing large parameter data to a REST call - rest

I have a REST Service that allows user to pass in a list of Properties they want returned from the call, eg:
/Item/123/Properties/Name,Id,Description,Type
There are hundreds of Property names that can be passed in, which then causes the issue that the number of chars supported between segments (eg: /IamASegment/) is 260 without changes to the registry etc.
So my question is when I need to support the user passing in large amounts of data like this, what is the best method, should it be passed in via the header?

A proper REST solution would be to create a form on the previous page/state and submit that form via POST, which in turn would generate a redirected GET to the actual parameterized resource. The parameter in this case could be some number for example that represents a bit-field for the requested fields.
Something like this:
GET /items
{"form": {
"Id": { "type": "number" },
"Name" : { "type": "checkbox" },
"Description" : { "type": "checkbox" },
...
}
POST /items
{"Name": "true", "Description": "true", ... }
Redirects to:
GET /items/123?fields=110110111
Of course you would have to define the proper media-types for the forms, requests, responses, etc.

Related

JSON API for non-resource responses

Currently, I'm working on new product and making REST API for both - public and internal needs. I started with {json:api} specification and I was pretty happy with it until I faced some questions I cannot find answers to.
According to JSON API specification, every resource MUST contain id.
http://jsonapi.org/format/
Every resource object MUST contain an id member and a type member. The values of the id and type members MUST be strings.
And that's fine in many cases but not all.
Most of our endpoints are about "resources"
If I ask for a "things" collection (http://example.com/things)
{
"data": [{
"type": "things",
"id": "1",
"attributes": {
"title": "first"
},
"links": {
"self": "http://example.com/things/1"
}
}, {
"type": "things",
"id": "1",
"attributes": {
"title": "second"
},
"links": {
"self": "http://example.com/things/2"
}
}]
}
If I ask for a single "things" resource (http://example.com/things/1)
{
"data": {
"type": "things",
"id": "1",
"attributes": {
"title": "first"
},
"links": {
"self": "http://example.com/things/1"
}
}
}
But what to do with endpoints which are not about resources and does not have ID?
For example, in our application, there is an endpoint http://example.com/stats which should return stats of current logged in user. Like
{
"active_things": 23,
"last_login": "2017"
}
There is no id for this "resource" (it's not actually a resource, is it?). Backend just collects some "stats" for logged in user and returns an object of stats. There many endpoints like this in this application, for example, we have Notification center page where the user can change email addresses for different notifications.
So frontend app (single-page-app) first has to get current values and it sends the request to GET http://example.com/notification-settings.
{
"notifications_about_new_thing": "arunas#example.com",
"notification_about_other_thing": "arunas#example.com"
}
And there are many more endpoints like this. The problem is - how to return these responses in JSONAPI format? There is no ID in these endpoints.
And the biggest question is - why nobody else is facing this issue (at least I cannot find any discussion about this)? :D All APIs I ever made has some endpoints which don't have "id".
I have two ideas, first is to fake id, like "id": "doesnt_matter", the second - do not use json-api for these endpoints. But I don't like both of them.
Think RESTfully and everything can (must) be a resource. There is no "logged in" user as there are no sessions in RESTful APIs as they are stateless. There's no session state maintained between REST API invocations, so you have to be explicit about who the user is.
In this case, the resource is the user who has some stats attributes (in the simple case) or perhaps a relationship to a separate stats relationship (more complicated, not shown):
GET /users/1234
{
"data": {
"type": "users",
"id": "1234",
"attributes": {
"name": "etc.",
"active_things": 23,
"last_login": "2017"
}
}
}
I'm no JSON API expert- but it's worth noting that while JSON API is a concrete specification, it is not the same thing as JSON, nor as a REST API. If you don't like its semantics, I agree with commenters who argue, "Don't use it." If you are going to use JSON API, do so in a compliant way, where every response is a resource; every resource has an ID and a type; and additional information is supplied as attributes of the resource.
Toward your question, I'm thinking about something similar where my application returns computation results. Now on the one hand, these are not strictly "resources" and so I've been toying with the idea of returning the raw result as an array (which I believe would be valid JSON, with a caveat), e.g:
[ 47 ]
On the other hand, there is the idea that the results are the results of a computation that the client specified RESTfully, in which case one of the following two cases is likely true:
The same request submitted later is likely to have the same result. This suggests that in fact the result really is a resource.
The same request submitted later is likely to have a different result. This suggests that the client may want to track how results change for various queries, and so at least the query parameters should be part of the response.
In both cases, the response really is a 'result' object, and even though it doesn't have an ID per se, it does have an identity. If nothing else fits, the ID could be the query that generated the response.
This seems RESTful to me. User #n2ygk suggests that this is not correct as regards the JSON API spec, that an ID should simply be a unique ID and not have another semantic interpretation.
I'd love to hear other perspectives.

RESTfully create or update a resource that references

If I wanted to create (POST) a new resource linking two independent resources, what is the most proper - with respect to HATEOAS and REST principles - way to structure the entity of the request?
Any references in RFCs, W3C documents, Fielding's thesis, etc., about the proper way for a client to request two independent resources be linked together would be most valuable. Or, if what I'm interested in is simply outside the scope of REST, HATEOAS, an explanation of why would also be great.
Hopefully my question above is clear. If not, here's a scenario and some background to ground the question.
Let's say I have two independent resources: /customer and /item, and a third resource /order intended to the two.
If I'm representing these resource to the client in a HATEOAS-like way (say with JSON-LD), a customer might (minimally) look like:
{
"#id": "http://api.example.com/customer/1"
}
and similarly an item like:
{
"#id": "http://api.example.com/item/1"
}
I'm more concerned about what scheme the entity of the POST request should have, rather than the URL I'm addressing the request to. Assuming I'm addressing the request to /order, would POSTing the following run afoul of HATEOAS and REST principles in any way?
{
"customer": {"#id": "http://api.example.com/customer/1"},
"item": {"#id": "http://api.example.com/item/1"}
}
To me, this seems intuitively OK. However, I can't find much or any discussion of the right way to link two independent resources with a POST. I discovered the LINK and UNLINK HTTP methods, but these seem inappropriate for a public API.
The client does not build URIs, so this is wrong unless these resource identifiers or at least their template came from the service. It is okay to use the id numbers instead of the URIs until you describe this in the response which contains the POST link.
An example from the hydra documentation:
{
"#context": "http://www.w3.org/ns/hydra/context.jsonld",
"#id": "http://api.example.com/doc/#comments",
"#type": "Link",
"title": "Comments",
"description": "A link to comments with an operation to create a new comment.",
"supportedOperation": [
{
"#type": "CreateResourceOperation",
"title": "Creates a new comment",
"method": "POST",
"expects": "http://api.example.com/doc/#Comment",
"returns": "http://api.example.com/doc/#Comment",
"possibleStatus": [
... Statuses that should be expected and handled properly ...
]
}
]
}
The "http://api.example.com/doc/#Comment" contains the property descriptions.
{
"#context": "http://www.w3.org/ns/hydra/context.jsonld",
"#id": "http://api.example.com/doc/#Comment",
"#type": "Class",
"title": "The name of the class",
"description": "A short description of the class.",
"supportedProperty": [
... Properties known to be supported by the class ...
{
"#type": "SupportedProperty",
"property": "#property", // The property
"required": true, // Is the property required in a request to be valid?
"readable": false, // Can the client retrieve the property's value?
"writeable": true // Can the client change the property's value?
}
]
}
A supported property can have an rdfs:range, which describes the value constraints. This is not yet (2015.10.22.) added to the hydra vocab as far as I can tell, but I don't have time to follow the project. I think you still can use the rdfs:range instead of waiting for a hydra range.
So in your case you could add an item property with a range of http://api.example.com/doc/#Item and so on. I assume you could add the links of the alternatives, something like http://api.example.com/items/, so you could generate a select input box. Be aware that this technology is not stable yet.
So you can send a simple JSON as POST body {item: {id:1}, customer: {id:1}} or something like that, which you generate based on the POST link. The RDF is for the client not for the server. The server can understand the data structure it requires, it does not need RDF. You don't need a dictionary to understand yourself...

Facebook: field expansion in batch requests ->

How can I define the requested fields for the 2nd part of my batch request?
Example:
[
{
"method": "GET",
"name": "get-friends",
"relative_url": "me/friends?limit=5",
"omit_response_on_success": false
},
{
"method": "GET",
"relative_url": "?ids={result=get-friends:$.data.*.id}"
}
]
This works so far. But now I want to define the requested fields for the 2nd part.
When I add &fields=address ("relative_url": "?ids={result=get-friends:$.data.*.id}&fields=address") I get a "Batch parameter must be a JSON array" exception :-(
Ralph
I get a "Batch parameter must be a JSON array" exception
That probably just means your query could not be understood, because you invalidated the syntax somehow. & has a special meaning in a URL, so you most likely just need to encode it properly.
URL-encode the whole value of the batch parameter (so basically your whole JSON string that you got from your object.)
(What function/method to use for that, depends on how you build your request, resp. in what language. PHP has urlencode, JavaScript has encodeURIComponent, etc.)

Master-detail representation in Json-LD

On forhand : sorry if I misunderstood hypermedia or Restfull concepts : it's a work in progress...)
I try to figure out hypermedia and hydra (http://www.markus-lanthaler.com/hydra), and have some questions about returning information to the client before designing my api.
say I have a webshop located at www.myshop.com
a HTTP GET to the root could return (for example) a list of resources represented as link (in a json-ld document):
...
"#id": "/api",
"products" : "www.myshop.com/api/products",
"customers":"www.myshop.com/api/customers"
...
First question on hydra, how could I add actions here ? it seems the client needs to load another document before the load of application. I mean the potential actions are not in the docuemnt retrieved from www.myshop.com/api Or do I miss something?
Then going further, I've stated that products is a hydra:Link so that the client could follow that link (interact with it) with a HTTP GET and retrieve a list of products. that will be a list like this :
....
{
"#id": "/api/products/123",
"#type": "vocab:Product"
},
{
"#id": "/api/products/124",
"#type": "vocab:Product"
},
....
here the client receives a list of product (That could be a paged collection). But if the client wants to display it to the user, let's say a table with [product Id, price, name] (not all Product's properties)
Second Question : How could I do that without the client sending a request to the server for each product, but still provide the link to get the product's detailed information,(or even here having four link : one for getting the detailed information, one for Delete and one for sharing it with a friend and a last one to add it to a Basket) ?
In fact I have difficulties to figure out how hydra is coming into play by not having Links in the document itself? I think that Hal uses this approach to having links in the document itself (if I am right) and I try to find how hydra does this link...
regards
A bit late but I'll nevertheless try to answer your questions Cedric.
say I have a webshop located at www.myshop.com
a HTTP GET to the root could return (for example) a list of resources
represented as link (in a json-ld document):
... "#id": "/api",
"products" : "www.myshop.com/api/products",
"customers":"www.myshop.com/api/customers" ...
First question on hydra, how could I add actions here ? it seems the
client needs to load another document before the load of application.
I mean the potential actions are not in the docuemnt retrieved from
www.myshop.com/api Or do I miss something?
You basically have two options here: 1) embed the operations directly in the response or 2) attach the operations to the properties (products, customers) instead.
Approach 1) would look somewhat like this:
...
"#id": "/api",
"products" : {
"#id": "http://www.myshop.com/api/products",
"operation": {
"#type": "Operation",
"method": "POST",
"expects": "Product"
}
}
...
While approach 2) would attach the same operation to the products property in the referenced Hydra ApiDocumentation:
...
"#id": "...products",
"supportedOperation": {
"#type": "Operation",
"method": "POST",
"expects": "Product"
}
...
Please note that in 1) I used operation while in 2) I used supportedOperation. Also, you should use a more specific type than Operation.
Regarding your second question:
with a HTTP GET and retrieve a list of products. that will be a list like this :
....
{
"#id": "/api/products/123",
"#type": "vocab:Product"
},
{
"#id": "/api/products/124",
"#type": "vocab:Product"
},
....
here the client receives a list of product (That could be a paged
collection). But if the client wants to display it to the user, let's
say a table with [product Id, price, name] (not all Product's
properties)
Second Question: How could I do that without the client sending a
request to the server for each product, but still provide the link to
get the product's detailed information,(or even here having four link
: one for getting the detailed information, one for Delete and one for
sharing it with a friend and a last one to add it to a Basket) ?
You can add as much information (including links) as you want directly in the collection.
....
{
"#id": "/api/products/123",
"#type": "vocab:Product",
"name": "Product 123",
"price": "9.99"
},
{
"#id": "/api/products/124",
"#type": "vocab:Product",
"name": "Product 124",
"price": "19.99"
},
....
That way, a client only needs to dereference an item if the collection doesn't contain that required information.
In fact I have difficulties to figure out how hydra is coming into
play by not having Links in the document itself?
Of course you do have links in the document as well. Links are just properties whose values happen to be URLs (objects with an #id property unless you set the property's type to #id in the context to get rid of that) instead of treating them specially.
note: The Hydra part of the answers I am not so sure, the JSON-LD and REST are okay I think.
You can use #base and relative IRIs by JSON-LD, or you can define namespaces in the #context, so after that you can use relative IRIs as ns:relativeIRI. Each one is better than returning the full IRI. (It is easier to parse the results with a general JSON-LD parser on client side, instead of a simple JSON parser.)
You can define your own #vocab using the Hydra vocab, or you can add "action" definitions in the #context. If you want to "add actions" you have to use hydra:Operation sub-classes in your vocab. Something like this (but I am not a Hydra expert):
{
"#id": "vocab:ProductList",
//...
"hydra:supportedOperations": [
{
"#type": "hydra:CreateResourceOperation",
"method": "POST",
"expects": "vocab:Product"
}
//...
]
}
In general by REST, if you need the same resource with fewer properties, then you have to add a new IRI for that resource, e.g.: /myresource?fewer=1. For example in your case: /api/products/?fields="id, price, name" is okay.
By Hydra you have 2 choices if you want multiple links; you can add a new hydra:Link as a property, or you can add a new hydra:Operation as a supportedOperation with method: GET. I guess get operations are for something like search which has an user input, but if you don't want to add a new property for each link, I think you have no other option.
Actually Hydra does have link and operation support. Maybe it is not clear, but JSON-LD is an RDF format, you can define RDF triples in that. So the IRIs you used for example by "customers":"www.myshop.com/api/customers" are just resource identifiers and not links. A link should have IRI, title, method(GET), language, content-type, iana:relation, etc... so it is not possible to describe a link you can follow with just a single IRI (resource identifier). By processing a REST resource a client should never check the IRI structure to know how to display what it got from you. You have to check the other properties of the links, especially iana:relations or by Hydra maybe operation type to do that. So for example in your case www.myshop.com/api/dav8ufg723udvbquacvd723fudvg is a perfectly valid IRI for the list of the customers. We use nice IRIs only because it is easier to configure generate them on server side, and configure a router for them.
Please check the Hydra vocab before further questions. As you can see a Class can have supportedOperations and supportedProperties which are both collections. A Link is a Property sub-class which can have a single Operation. By collections I think you have to use the Collection class, in which member contains the items of the collection... Be aware that by JSON-LD there is no difference by defining a single item or multiple items with the same type. In the context you have to define only the type, and the value of the property can contain both a single item or an array of items... If you want some constraints about that I guess you have to add some OWL triples, and a validator which checks the values using them.

Can't add likes to the fields param

According to the Facebook Graph API documentation, the fields param acts as a result mask:
By default, most object properties are returned when you make a query.
You can choose the fields (or connections) you want returned with the
"fields" query parameter.
Indeed, this works fine for most fields. For instance, /7354446700?fields=name,picture returns:
{
"name": "Grooveshark",
"id": "7354446700",
"type": "page",
"picture": "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-snc4/203560_7354446700_6819703_q.jpg"
}
However, for some reason, as soon as the likes field is added to the fields list, things break down. For instance, /7354446700?fields=name,picture,likes returns:
{
"name": "Grooveshark",
"id": "7354446700",
"type": "page",
"picture": "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-snc4/203560_7354446700_6819703_q.jpg",
"likes": {
"data": [
]
}
}
Even more strange, if I omit the other two fields (name and pictures), sending only likes, I get
{
"likes": {
"data": [
]
}
}
The reason I find this extra-strange is because the "mandatory" fields (id and type) which should be added to every response are not included here (although they were included when fields=name,picture,likes).
What appears to be happening is that the field=likes parameter appears to be misinterpreted as a Connections request rather than simply a field mask, hence the data segment that normally appears when you'd call /7354446700/likes.
Is there a good reason for this? Is there any other way to get the likes field without fetching the entire object? I can't imagine this would be expected behavior, so I assume it is a bug, but I thought I'd ask here first before filing one.
This indeed appears to be a bug; I've checked internally and there's an as yet unresolved task open to fix this issue which was reported to us in our bug tracker previously.
In the meantime, the default return value for a page will include the 'likes' field even if it cant be retrieved solely.