Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I am working on an REST API and I am trying to understand how to deal with hierarchical resources.
Background
Let's start with a simple example. In my API I have Users, User profiles and Reviews.
Users must have a User Profile associated (a User Profile corresponds to only one User)
Users might have a Review associated (a Review corresponds to only one User)
User's resource representation should be:
User: {
"u1": "u1value", // User's attributes
"u2": "u2value",
...
"links": [{
"rel": "profile",
"href": "http://..." // URI of the profile resource
}, {
"rel": "review",
"href": "http://..." // URI of the review resource
}]
}
User profile resource representation should be:
UserProfile: {
"p1": "p1value", // Profile attributes
"p2": "p2value",
...
"links": [{
"rel": "owner",
"href": "http://..." // URI of the user resource
}]
}
Review resource representation should be:
Review: {
"r1": "r1value", // Review attributes
"r2": "r2value",
...
"links": [{
"rel": "owner",
"href": "http://..." // URI of the user resource
}]
}
Resources URIs could be:
http://api.example.com/users/{userid}: access to the user resource
http://api.example.com/users/{userid}/profile: access to the user's profile resource
http://api.example.com/users/{userid}/review: access to the user's review resource
Resource creation: what's the correct way to create a user?
Now I want to create a new user:
POST http://api.example.com/users {"u1": "bar", "u2": "foo"} and I get back the new userid = 42
POST http://api.example.com/users/42/profile {"p1": "baz", "p2": "asd"}
PUT http://api.example.com/users {"u1": "bar", "u2": "foo", links: [{"rel": "profile", "href": "http://api.example.com/users/42/profile"]}
My concerns:
What if something breaks between 1 and 2 or 2 and 3?
In 3), should the server update automagically the links in the http://api.example.com/users/42/profile, to point to the correct owner?
Updating link fields is the proper manner to create relationships? Or should I skip step 3) and let the system guess the relationships according to URI conventions? (I read on several books that URI should be considered as opaque.)
Your concerns are well-placed and your list of issues is correct. If I may suggest, your approach looks very much like you are using a relational DB approach and are doing an INSERT, retrieving the PK from a sequence which you use for the next INSERT, and so on.
Let the server maintain referential integrity
As an observation, even if following your original scheme, omit step 3 entirely. The URI in links that is visible when you retrieve your user document should be generated by the server based on the existence of the profile record.
For example, if using a relational backend, you SELECT from USERS to get the user record. Next, you SELECT from PROFILES. If there is a record, then you modify the return datastructure to include the reference.
POST entire documents
A common way to resolve the other issues that you bring up is to allow posting of an entire document to the user URL (like NoSQL databases such as MongoDB). Here the document is the user and the profile:
{
"u1": "bar",
"u2": "foo",
"profile": {
"p1": "baz",
"p2": "asd"
}
}
In this approach, your end-point on the server receives a nested structure (document) and performs the INSERT into USERS, retrieves the PK, then performs the INSERT into PROFILES using this PK. Doing this on the server side resolves several concerns:
The transaction can be atomic
There is only one network exchange between client and server
The server does the heavy lifting and can validate the entire transaction (eg the user is not created if the profile is not valid)
No need for step 3.
Note that you this approach is in addition to the APIs you have detailed above - you still want to be able to directly access a user's profile.
GET - client can specify fields
It is interesting to compare with APIs from well-established companies. Take LinkedIn for example. In their developer API the default GET for a user returns simply the user's name, headline and URI.
However, if the request specifies additional fields, you can get the nested data, eg the second example in http://developer.linkedin.com/documents/understanding-field-selectors returns the user's name and a list company names for the positions they have held. You could implement a similar scheme for Profiles and Reviews.
PATCH for updating document properties
With inserting and querying out of the way, it might be worth considering how to update (PATCH) data. Overwriting a field is obvious, so you could, for example PATCH to http://api.example.com/users/42 the following:
{
"u1": null,
"u2": "new-foo",
"profile": { "p1": "new-baz"}
}
Which would unset u1, set u2 to new-foo and update the profile's p1 to new-baz. Note that if a field is missing (p2), then the field is not modified. PATCH is preferable to the older PUT as explained in this answer.
If you only need to update the profile, PATCH the new profile record directly to http://api.example.com/users/42/profile
DELETE should cascade
Lastly, deleting can be done with the DELETE method pointing to the resource you want to delete - be it User, Profile or Review. Implement a cascading delete so that deleting a User deletes his/her Profile and Reviews.
You should stick to HATEOAS, and dereference the URLs you get on your responses:
For ease of access, lets say User.profile contains the href of the link with rel == profile.
Create the User
With the POST you described... but it should not return an id, but a user, complete with it's links.
User: {
"u1": "bar", // User's attributes
"u2": "foo",
...
"profile": "http://api.example.com/users/42/profile",
"links": [{
"rel": "profile",
"href": "http://api.example.com/users/42/profile"
},
...
]
}
At this point the profile resource at User.profile (could be http://api.example.com/users/42/profile, or whatever location you migrate to in the future) is be whatever a default profile should be, e.g. an empty document or with just the owner link filled.
Update the Profile
profile = GET User.profile
profile.p1 = "baz"
profile.p2 = "asd"
PUT profile to the same url you just dereferenced
By dereferencing hrefs on your documents instead of constructing urls with id's you get from responses, you client will not have to change when the API changes. Like when
- profiles are moved to http://profiles.cdn.example.com/
- profiles get a p3 value
"Old" API clients will keep working without having to change any code.
Actually from successful step (1) you should get HTTP code 201 Created and an address (URL) to the newly created resource, not just the ID number. If step (2) fails your REST API should indicate whether the problem is with the client such as badly formed document (issue code 4xx) or server (5xx). For example if in the meantime resource 42 was deleted, code 404 Not Found should be returned.
Therein lies the problem with stateless REST APIs - they cannot support transactions composed of more than one request. For this to be possible you would have to maintain a session (state) on the server.
By the way, the URL in step (3) in your example suggest that you are substituting all users and probably should read http://api.example.com/users/42.
You have a choice between submitting complete user+profile document at once to be split into two database records in one atomic transaction, or to allow persistence of partial user data i.e. user without a profile.
The choice depends on the context. For example it may be perfectly fine that a user does not have a profile (so it can be provided by the user). Conversely having a profile record, which does not belong to any user is probably not acceptable. Discussion about enforcing this logic goes beyond the scope of your question and will vary by the type of persistent store (database) you choose. Relational databases enforce this using foreign keys.
I believe your calls should be like this
1) Create a user
POST http://api.example.com/users + params in payload
If it returns HTTP 201 + user id, then you can go on and create the profile. Else you treat the exception the way you want. You should wait for the first call to get back before starting the creation of the profile.
2) Create a profile associated to a user 42 (if the user creation was ok)
POST http://api.example.com/users/42/profile + params in payload
returns HTTP 201 + profile id
Your backend will be responsible for updating your user object and the profile object (and your database) so that users 42 will be linked to the new profile. If the backend cannot link the object you can send back a 500 error explaining what happened.
So in my opinion I would skip step 3.
Now I understand that your point is about the fact that a user MUST have a profile
I see 2 solutions
1) you could create an empty associated profile when creating your user. Then by querying the user you can get the profile id and modify it with a PUT (I really do not like this solution because when you are asking your api to create a user it should only create a user and nothing else but as in fact a profile is mandatory it not as ugly as it seems).
2) you could have a property in you user saying if the user is correct or not (meaning he has an associated profile). At the end of the creation process if your user 42 is not correct you can delete it or retry the profile creation ... Then you could query only correct users with something like /users?isCorrect=true
3) Let the client treat the fact that a user as no profile -> show a popup to ask for creation of the profile ...
Have a look to this document for REST API best practices
Maybe you could also have a look to HAL which tries to deal with relationship between objects.
And last but not least you could follow api-craft google group you might find interesting topics related to your issue there.
Related
How I can update the inventory (stock_level) using (business manager API).
I use the business manager API to retrieve products. I am able to retrieve the products but I am not sure how I can set its stock (stock_level).
I have a special requirement where product quantity cannot exceed 1, so for that I need to set it in an inventory so that I can test it.
I have tried to see if I can set inventory level using product but that doesn't seem possible.
When I try to get inventory following is the error
{
"_v": "18.8",
"fault": {
"arguments": {
"method": "GET",
"path": "/data/v18_8/inventory_lists/*"
},
"type": "ClientAccessForbiddenException",
"message": "Access to resource 'GET /data/v18_8/inventory_lists/*' is not allowed for the current client."
}
}
There is actually a DATA API endpoint that can be used to update inventory. It is called the ProductInventoryRecords resource.
You can update a product inventory record with a PATCH eg:
PATCH /inventory_lists/{inventory_list_id}/product_inventory_records/{product_id}
With a ProductInventoryRecordAllocation payload as such:
{
"_resource_state" : "847f9c3c5867f641470b3046aeec31f07757991b792d722e10079926f7a289fb",
"allocation": {
"amount": 2000,
"reset_date": "2016-03-31T14:05:40.872Z"
}
}
See more about this document type here.
Please note that the best practice is to pass the _resource_state key to ensure that the record is properly updated. OCAPI checks to see if this value is the same as the current state of the record if that attribute provided.
So systems should first check the record to get the _resource_state by performing a GET on the same resource.
Edit Note that you'll need an authorization token that grants you access to the API in order to make this kind of call.
your question is not crystal clear but I will try to answer. Commerce Cloud has three distinct (OCAPI) APIs:
Shop API (provides similar access as a customer on the site)
Data API (provides similar access as a merchant using business manager)
Meta API (describes the API from a functional perspective)
To get availability of a product in the inventory use below call: {{shop_url}}/products/701644676568M/availability and look at ATS in the response.
To set the stocklevel go into to business manager or use business manager import utility. There is no out-of-the-box API to update the stocklevel.
I'm designing a REST interface for my service. My service will be Role and Permission based. Therefor a Role will contain Permissions.
Meaning a Role will have a collection of Permissions -> roles/1/permissions
Now I would like to change the set of Permissions for a Role. As I understand, a correct request for this would be:
PUT roles/1/permissions
[
{
id: 1,
permissionName: "read"
}, {
id: 2,
permissionName: "write"
}, {
id: 3,
permissionName: "delete"
}
]
As this basically only maps existing permissions (/permissions) to a role, there is a lot of unnecessary information sent. Especially if this example would contain a resource which is much more detailed as a Permission.
Would it be okay to only send an array of ids in the body?
PUT roles/1/permissions
{
ids:[1,2,3]
}
I would reach the same, with only the information I actually need. Is there a problem with this approach? I'm afraid that this might hurting the stateless principle of REST.
Sending the permission identifiers is just fine to create a link between the a given role and a set of permissions.
On server side, you should validate the permission identifiers and return a 422 if they are invalid. See this answer for further details.
The stateless constraint determines that each request must contain all of the information necessary to be understood by the server, rather than be dependent on the server remembering prior requests. And session state should be handled entirely by the client:
5.1.3 Stateless
[...] each request
from client to server must contain all of the information necessary to
understand the request, and cannot take advantage of any stored
context on the server. Session state is therefore kept entirely on the
client. [...]
Sending only the permission identifiers won't break the stateless constraint.
For more details on the stateless constraint, refer to this answer.
Your REST API is completely fine and it does not violate the stateless principle in any way. Your REST API will be using a different resource model for GET and PUT methods, which is also fine.
I am looking to expose some domain RESTful APIs on top of an existing project. One of the entities I need to model has a single document: settings. Settings are created with the application and is a singleton document. I'd like to expose it via a well-designed resource-based RESTful API.
Normally when modeling an API for a resource with many items its something like:
GET /employees/ <-- returns [] of 1-* items
GET /employees/{id}/ <-- returns 1 item
POST /employees/ <-- creates an item
PUT /employees/{id}/ <-- updates all fields on specific item
PATCH /employees/{id}/ <-- updates a subset of fields specified on an item
DELETE /employees/{id}/ <-- deletes a specific item
OPTION 1: If I modeled settings in the same way then the following API is built:
GET /settings/ <-- returns [] of 1-* items
[{ "id": "06e24c15-f7e6-418e-9077-7e86d14981e3", "property": "value" }]
GET /settings/{id}/ <-- returns 1 item
{ "id": "06e24c15-f7e6-418e-9077-7e86d14981e3", "property": "value" }
PUT /settings/{id}/
PATCH /settings/{id}/
This to me has a few nuances:
We return an array when only 1 item CAN and EVER WILL exist. Settings are a singleton that the application creates.
We require knowing the id to make a request only returning 1 item
We require the id of a singleton just to PUT or PATCH it
OPTION 2: My mind then goes in this direction:
GET /settings/ <-- returns 1 item
{ "id": "06e24c15-f7e6-418e-9077-7e86d14981e3", "property": "value" }
PUT /settings/
PATCH /settings/
This design removes the nuances brought up below and doesn't require an id to PUT or PATCH. This feels the most consistent to me as all requests have the same shape.
OPTION 3: Another option is to add the id back to the PUT and the PATCH to require it to make updates, but then an API user must perform a GET just to obtain the id of a singleton:
GET /settings/ <-- returns 1 item
{ "id": "06e24c15-f7e6-418e-9077-7e86d14981e3", "property": "value" }
PUT /settings/{id}/
PATCH /settings/{id}/
This seems inconsistent because the GET 1 doesn't have the same shape as the UPDATE 1 request. It also doesn't require a consumer to perform a GET to find the identifier of the singleton.
Is there a preferred way to model this?
Does anyone have any good reference material on modeling RESTful APIs for singleton resources? I am currently leaning towards OPTION 2 but I'd like to know if there are good resources or standards that I can look into.
Is there a compelling reason to require an API consumer to make a GET for the id of a resource to then use it in an update request, perhaps for security reasons, etc?
The ID of the Resource is the Url itself and not necessarily a Guid or UUID. The Url should uniquely IDentify the Resource, in your case the Settings entity.
But, in order to be RESTfull, you must point to this resource in your index Url (i.e. the / path) with an appropriate rel attribute, so the client will not hardcode the Url, such as this:
GET /
{ ....
"links": [
{ "url" : "/settings", "rel" : "settings" }
], ...
}
There are no specifics to accesing a singleton resource other than the Url will not contain a Guid, Uuid or any other numeric value.
Option 2 is perfectly RESTful, as far as I can tell.
The core idea behind RESTful APIs is that you're manipulating "resources". The word "resource" is intentionally left vague so that it can refer to whatever is important to the specfic application, and so that the API can focus only on how content will be accessed regardless of what content will be accessed.
If your resource is a singleton, it does not make sense to attribute an ID value to it. IDs are very useful and commonly used in RESTful APIs, but they are not a core part of what makes an API RESTful, and, as you have noticed, would actually make accessing singleton resources more cumbersome.
Therefore, you should just do away with IDs and have both
GET /settings/
and
GET /settings/{id}
always return the settings singleton object. (access-by-id is not required, but it's nice to have just in case someone tries it). Also, be sure to document your API endpoint so consumers don't expect an array :)
Re: your questions,
I believe option 2 would be the preferred way of modeling this, and I believe requiring your consumer to make a GET for the id would actually be somewhat of an anti-pattern.
I think the confusion here is because the word settings is plural, but the resource is a singleton.
Why not rename the resource to /configuration and go with option 2?
It would probably be less surprising to consumers of your API.
You're probably overthinking it. There's no concept of singleton in HTTP or REST.
GET /settings/ is perfectly fine.
By the way, we can hardly relate this to DDD - at least not if you don't give more context about what settings means in your domain.
It might also be that you're trying to tack an "Entity with ID" approach on Settings when it's not appropriate. Not all objects in a system are entities.
Lets say I have the following classes:
company
user
address
a company object can contain users and addresses,
a user object contains addresses
company can exist on its own, but users and address is always a part of company or user (a composition in UML wording):
company ->
addresses
users ->
addresses
Now I want use the right REST-structure for POST (insert) and PUT (update) requests. What would be the correct approach?
variant 1:
// insert address:
POST /api/company/{companyId}/addresses
POST /api/company/{companyId}/users/{userId}/addresses
// update address:
PUT /api/company/{companyId}/addresses/{addressId}
PUT /api/company/{companyId}/users/{userId}/addresses/{addressId}
variant 2:
// insert address:
POST /api/address // companyId, userId etc. as parameter
// update address:
PUT /api/address/{addressId}
My personal gut feeling would be to use variant 1 for creation (POST) and variant 2 for updates (PUT), because variant 1 seems "cleaner" for creation but for update variant 2 would be better because it does not require a parentId (company or user)
First, REST is an architectur style not a protocol. This means that there is no right or wrong in how you define your URI. The only requirement Roy Fielding put in place was that each resource has to have a unique resource identifier (URI). The operation you may do on those URIs (verbs) are defined by the underlying hypertext transfer protocol (HTTP).
Second, there are certain best practices like using a sub-resource for resources that are embedded in other resources (especially if they can't exist without the parent resource) as in your case with addresses and users/companies
As you have certain issues with updating a sub-resource:
Usually to update a resource HTTP verb PUT is used which has the limitation of replacing the current state (all the available data) with the state you've sent to the server (in case the update goes well). As there is no partial update yet defined in HTTP some use the PUT verb a bit fuzzy and only update what is available within the request, though issuing a PATCH request and only updating these fields is probably more correct in terms of HTTP specification.
Sub-resources are very similar to regular resources. If you update a sub-resource you don't need to update the parent resource. In your particular case, if you want to update a user's address but not the user itself, you issue a PUT /users/{userId}/addresses/{addressId} HTTP/1.1 request containing the new state of the address to the server. The request body may look like this:
{
"street": "Sample Street 1"
"city": "Sampletown",
"zip": "12345",
"country": "Neverland",
"_links": {
"self": { "href": "/users/{userId}/addresses/{addressId} }
"googleMap": { "href": "https://www.google.com/maps/place/Liberty+Island/#40.6883129,-74.042817,16.4z/data=!4m2!3m1!1s0x0000000000000000:0x005f3b2fa5c0821d" }
}
}
If you want to follow the HTTP PUT verb closely and have dynamic fields, which may appear during runtime, you might have to alter the current table definition eventually, delete the old address entry and insert a new entry with the provided information (depending on the DB layer you are using SQL vs NoSQL). Note the HTTP PUT verb semantic!
On utilizing a fuzzy update strategy (=partial update) a simple update statement should be fine. Here you can simply ignore the additional userId contained within the URI, though this is not (yet) fully HTTP compliant - at least in regards to the spec. In that particular case it is your choice which version of URI you choose, though version 2 only works in the latter case while version 1 works in both cases.
In my models I have Events and Users. Each Event has a leader attribute that points to a User, however there isn't always a host necessarily, in which case host is null.
In my API:
GET /users/ returns a list of all users
GET /users/3/ returns user 3 info, {"name": "John", "href": "/users/3/"}
GET /events/ returns a list of all events
GET /events/2/host/ should return the user who is a host for event 2. (say this is user 3)
Should I return {"href": "/users/3/"}, simply a link to the User? Or the representation of the user itself, {"name": "John", "href": "/users/3/"}?
Lets say I go with the first option. How do I change the user?
PUT /events/2/host/ json={"href": "/users/2/"} - does this make sense? My server would then have to parse the endpoint to figure out which user id is the new host, and then assign that in the database. Seems a bit inelegant, but Flask has a way to parse the endpoint into the arguments. But is this the right way to do it?
Finally, in the GET /events/2/ resource, I was thinking about having an attribute "host":{"href": "/events/2/host/"} in the representation. Does this make sense? And if there's no host, there would be no href attribute at all, just an empty dictionary assigned to host.
To be short, I think that you should return the representation when getting, for example, host for an event and use the link when updating it. But the design remains up to you ;-)
Here are the different cases:
GET /events/2/host/
{"name": "John", "href": "/users/3/"}
PUT /events/2/host/
{"href": "/users/3/"}
DELETE /events/2/host/ (to set the host field to null)
I think that the OData v4 specification could give you some good ideas for your design. This link gives good insights about the way to handle entity relations with OData : http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/entity-relations-in-odata-v4.
Moreover with its query parameter $expand, OData allows to configure if you load the entity dependency. If the field host is for example defined, you will have the content of the referenced entity, if not, only the reference.
OData also provides a way to get the reference for a dependency with the suffix $ref.
Another link could also give you some ideas: http://templth.wordpress.com/2014/12/15/designing-a-web-api/.
Hope it helps you.
Thierry