I'm rather new to REST so forgive me if this is a stupid question.
So, I have a customer resource. A customer has many credits. So, I imagine a URL for getting customer credits would be
customer/21/credits
(where 21 is a customer ID)
Now, how do I add to the credits, if I don't have the full amount of credits? E.g. a customer has 10 credits and I want to add 5. As I understand, if I'm using post I would do something like:
customer/21/credits?amount=15 (is this even correct?)
However, what if I just want to add to the existing credits? That is I want to send 5 credits and say add them to whatever the customer currently has? Do I define a kind of phantom resource such as addedCredits?
customer/21/addedCredits?amount=5
then behind the scenes, I just do credits += 5?
You need to define how you're going to treat "credits" in your system; it matters whether or not you intend to define them as resources or as an attribute of your customer resource.
In the examples below, I'm going to use XML to represent the resources/entities. This may work for you, but you'll need to have some consistent way of representing your resources in requests and responses - this will help you avoid using query parameters (e.g. http://example.com?foo=bar) to define data that belongs in the request body.
A couple of ways of representing credits:
If a "credit" is an attribute of your "customer":
<customer id="21">
<balance>10</balance><!-- aka credit -->
</customer>
Then you might as well just GET the customer, update the credit/balance with your client, and then PUT the <customer> back to /customer/21.
If a "credit" is its own resource:
You can POST the following to /credit:
<credit>
<dateApplied>2009-10-15 15:00:00</dateApplied>
<customer href="/customer/21"/>
<amount>5</amount>
</credit>
Or you can POST the following to /customer/21/credits (assuming that URI is a list of all of the <credit>s applied to the customer):
<credit>
<dateApplied>2009-10-15 15:00:00</dateApplied>
<amount>5</amount>
</credit>
This would "append" a new <credit> to the existing list. And also eliminates the need to provide the <customer> in the entity, since it's already present in the URI.
I would use the same URL.
POST to customer/21/credits with a POST variable called extraCredit set to 5. POST is supposed to be used for annotation of existing resources (or creating subordinate resources). There is no reason why you should need a new URL.
If an individual credit is a resource in your system that deserves its own URL, then the response URL from POSTing to customer/21/credits should include the URL of the new credit resource e.g. customer/21/credit/12.
You could define an XML representation of credits to POST to customer/21/credits, but I would not consider it worthwhile in this simple example. REST payloads do not have to be XML.
A URL like customer/21/addedCredits?amount=5 doesn't make sense to me because it doesn't really identify a resource. If someone issues a GET to customer/21/addedCredits?amount=5 what would you return to them?
The one thing you should definitely not do is change the state of the customer resource when someone GETs a URL like customer/21/addedCredits?amount=5. Since the title of your question acknowledges that you will need to use POST your probably realise this. GET is supposed to be safe, which means that issuing a GET shouldn't change a resource's state.
Ultimately the implementation is up to you. WHile URI query parameters are generally frowned upon, it doesn't mean you can't use them. Personally I would make the post URI something like:
customer/21/credits/add/5
but nothing says you can't do something like what you have or :
customer/21/credits/add?value=5
For starters using the URI query parameters should be a "bad smell" to you. Second you need to look at defining some mime types that your clients and server can talk in so for instance with your credit example:
If I do a GET on customer/21/credits I might get a document like this:
Content-type : application/vnd.creditstore+xml
<credits>
<user>21</user>
<credits>10</credits>
Add credits to this account
</credits>
This tells a client who understands your vocab that if they want to add credits to this user they need to post something to that link. This is HATEOAS (god I hate that acronym, I probably even spelled it wrong).
Now this is all completely off the top of my head, and I probably butchered that XML example but it should get you thinking in the right direction.
Related
I am creating a REST API for the Order screen. I have methods:
GET /api/orders
GET /api/orders/{orderId}
I have some buttons on the Order page and I created few endpoints for that:
PATCH /api/order/buttons/mark-as-read
PATCH /api/order/buttons/change-status
Now I need to add the delete button. But I don't understand how to do that. I have 2 options:
DELETE /api/orders/{orderId} - but I should send 2 additional parameters in this request
PATCH /api/order/buttons/delete - I can send my DTO in the body, but it is not a REST approach.
I want to understand which type of request is used for the delete button in the REST context?
PATCH /api/order/buttons/mark-as-read
PATCH /api/order/buttons/change-status
These are a bit strange. PATCH is a method with remote authoring semantics; it implies that you are making a change to the resource identified by the effective target URI.
But that doesn't seem to be the case here; if you are expecting to apply the changes to the document identified by /api/orders/{orderId}, then that should be the target URI, not some other resource.
PATCH /api/orders/1
Content-Type: text/plain
Please mark this order as read.
PATCH /api/orders/1
Content-Type: text/plain
Please change the status of this order to FULFILLED
Of course, we don't normally use "text/plain" and statements that require a human being to interpret, but instead use a patch document format (example: application/json-patch+json) that a machine can be taught to interpret.
I want to understand which type of request is used for the delete button in the REST context?
If the semantics of "delete" belong to the Orders domain (for instance, if it is a button that signals a desire to cancel an order) then you should be using PUT or PATCH (if you are communicating by passing updated representations of the resource) or POST (if you are sending instructions that the server will interpret).
The heuristic to consider: how would you do this on a plain HTML page? Presumably you would have a "cancel my order" form, with input controls to collect information from the user, and possibly some hidden fields. When the user submits the form, the browser would use the form data and HTML's form processing rules to create an application/x-www-form-urlencoded representation of the information, and would then POST that information to the resource identified by the form action.
The form action could be anything; you could use /api/orders/1/cancel, analogous to your mark-as-read and change-status design; but if you can use the identifier of the order (which is to say, the resource that you are changing), then you get the advantages of standardized cache invalidation for free.
It's normal for a single message handler, which has a single responsibility in the transfer of documents over a network domain, ex POST /api/orders/{orderId}, to interpret the payload and select one of multiple handlers (change-status, mark-as-read, cancel) in your domain.
you offer to use something like this: PATCH /api/orders/{orderId} and OrderUpdatesDto as JSON string in the request body?
Sort of.
There are three dials here: which effective request URI to use, which payload to use, which method to use.
Because I would want to take advantage of cache invalidation, I'm going to look for designs that use: /api/order/{orderId} as the effective request URI, because that's the URI for the responses that I want to invalidate.
It's fine to use something like a JSON representation of an OrderUpdate message/command/DTO as the payload of the request. But that's not really a good match for remote authoring. So instead of PATCH, I would use POST
POST /api/orders/1 HTTP/1.1
Content-Type: application/prs.pavel-orderupdate+json
{...}
But you can instead decide to support a remote authoring interface, meaning that the client just edits their local copy of /api/order/1 and then tells you what changes they made.
That's the case where both PUT (send back the entire document) and PATCH (send back a bunch of edits) can make sense. If GET /api/orders/1 returns a JSON document, then I'm going to look into whether or not I can support one of the general purpose JSON patch document formats; JSON Patch or JSON Merge Patch or something along those lines.
Of course, it can be really hard to get from "changes to a document" to a message that will be meaningful to a non-anemic domain. There are reasons that we might prefer supporting a task based experience, but sending a task centric DTO is not a good fit for PUT/PATCH if you also want caching to work the way I've described above.
I have the resource /contracts with the following structure:
{
name: "Contract name",
signedAt: 123213231,
// etc.
}
While basic CRUD operations are well clear (GET /contracts, GET /contracts/{id}, POST /contracts, etc.) some doubts come when I have to do some concrete actions on the resource.
One of these actions is the following:
sign: means the contract is signed, so the signedAt date will need to be updated with the moment (date-time) the contract was signed.
So far I've been thinking about these different approaches:
PATCH-ing the resource
This approach will mean having the following endpoint method:
PATCH /contracts/{id}
and just posting the signedAt date { signedAt: 123213231 } meaning that after this the contract will be signed.
What I don't like about this approach is that the signature date comes from the client, I was thinking that having this date initialized on the backend side whenever a contract is signed could be better and more consistent.
Totally discarded, as the signedAt date should be set on the server
side exactly at the moment the sign is done.
POST-ing a new resource
This approach will mean having the signature action as a resource:
POST /contracts/{id}/sign
with an empty body in this case as we don't need to pass anything else so, once it is posted, the backend side would be the responsible for having the signature date initialized.
POST-ing the resource using 'action'
Similar to the previous approach, in this case I would use a query parameter called action on the contract resource:
POST /contracts/{idContract}?action=sign
also with an empty body where ?action=sign. Like in the previous approach, once posted the backend side would be the responsible for having the signature date initialized.
Questions
What would be the proper way to have this designed at a REST API level?
Is any of the approaches above close to a good design or not?
Would I need to modify any of the approaches?
Is there any better alternative?
I have designed a few rest APIs myself but I am not a restful evangelist so my answer might not be the best. I would suggest some of the following:
Create a custom converter for date values in your rest service that accepts date AND other specific fields. If you checkGoogle reporting APIs for example they allow you to use specific date range and also CURRENT_WEEK, CURRENT_MONTH etc. So you can add such specific value and use it. For example PATCH signedAt=CURRENT_DATE and the API handles that properly.
Add a boolean signed field to the resource. Do a POST or PATCH with signed=true. This way you will be able to eventually query only signed resources easily ;) Also it might be the case that people care only about if it is signed than when it was signed
I wouldn't use ?action=sign or /contracts/{id}/sign because these are not RESTFUL and even if you do use GET and POST you would use them in a way to create a workaround in order to implement actions in your design which shouldn't have actions
just posting the signedAt date { signedAt: 123213231 } meaning that after this the contract will be signed.
On HTTP Patch
The set of changes is represented in a format called a "patch document" identified by a media type.
Rather than rolling your own bespoke media type, you might want to consider whether one of the standard formats is suitable.
For example: JSON Patch.
Content-Type: application/json-patch+json
[ { "op": "replace", "path": "signedAt", "value": 123213231 }
JSON Merge Patch is another reasonable option
Content-Type: application/merge-patch+json
{ signedAt: 123213231 }
From what I can see, the primary difference is that JSON Patch provides a test operation which gives you finer grain control than simply relying upon validators
But you are absolutely right - PATCH gives the client code authority to specify the time value. If that's not appropriate for your use case, then PATCH is the wrong tool in the box.
POST /contracts/{id}/sign
POST /contracts/{idContract}?action=sign
As far as REST/HTTP are concerned, these two choices are equivalent -- you are updating the state of one resource by sending an unsafe request to a different resource. There are some mild differences in how these spellings act when resolving references, but as request-targets, it doesn't make a difference to the client.
An option that you seem to have overlooked:
POST /contracts/{id}
action=sign
This has the advantage that, when successful, you get cache invalidation for free.
In a hypermedia API, the flow might go something like this: the client would GET the resource; because the resource hasn't been signed yet, the representation could include a form, with a "sign" button on it. The action on the form would be /contracts/{id}. The consumer "signs" the contract by submitting the form -- the agent gathers up the information described by the form, encodes it into the request body, and then posts the request to the server. The server responds success, and the client's cache knows to invalidate the previously fetched copy of the resource.
Let's suppose I have an Group of users and I want to add/delete users from the group. What I am confused about is what will be the best practice to design the urls. Here are the options
OPTION # 1
POST /groups/{groupId}/users -- The request body will contain the userId
DELETE /groups/{groupId}/users/{userId} -- The userId will be in the path and the request body will be empty
OPTION # 2
DELETE /groups/{groupId}/users -- The request body will contain the userId
POST /groups/{groupId}/users/{userId} -- The userId will be in the path and the request body will be empty
I believe both the answers are correct and I am guessing there is no right or wrong answer here, just personal preference.But I would like to know what is used wide-spread. I have been using OPTION # 1 because I read in some book (the name escapes me) that the data you are POSTing shouldn't be a part of the url while using DELETE there is no such best-practice restraint.
All inputs appreciated !
The first option is the most common, but that means nothing, since misconceptions about REST are widespread. As a matter of fact, #1 isn't REST at all, it's RPC pure and simple.
Adding a member to the collection can be done either through a POST to the collection /groups/{groupId}/users, with the location of the created resource returned in the Location response header, or through a PUT request to the final location /groups/{groupId}/users/{userId}. The POST should return a 201 Created response, and the PUT either that or 200 OK, if the resource already existed and was replaced by the new one.
To delete, the correct way is to use DELETE /groups/{groupId}/users/{userId}. It's not a matter of personal preference. POST is a method you use for operations that aren't standardized by the HTTP protocol. Simple deletion is standardized through the DELETE method. Implementing it through the POST method simply means you'll have to document that functionality, instead of relying on the standard itself. You'd use POST only if you are doing something fancy during the deletion, something that already requires the functionality to be documented.
The option 1 seems to be the most common one. I don't have the feeling that the option 2 is valid at all!
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
In my continuing quest to try and wrap my mind around RESTful-ness, I've come to another place where I'm not sure how to proceed. I set up a thought expiriment for myself where I'd design a simple voting system for a resource, much like how SO allows voting on questions. So, say my resource is an image, and I can get an image by an ID, like so:
http://www.mysite.com/images/123123
And in this example, that returns say, a JSON representation of an image, like so:
{
"URL":"http://www.mysite.com/images/123123.jpg",
"Rep":"100"
}
How would I design a way to "vote" on that image? I'd like two operations; up-vote and down-vote. The client shouldn't know how much weight each carries, because I'd like to have the award for an up-vote/down-vote be decided at the server level so I can change it anytime I like.
My first idea was to have something like this:
http://www.mysite.com/vote/images?image=123123
To that URL, one could POST something like the following:
{
"Vote":"UpVote"
}
But I'm wary of that - to me that says RPC in disguise. Would that be a poor way to design this? If so, what other designs could I try?
To be restful you should return something like this
{
"URL":"http://www.mysite.com/images/123123.jpg",
"Rep":"100"
"UpVoteLink":"http://blah, blah, blah",
"DownVoteLink":"http://blah, blah, something else blah",
}
As far as REST is concerned it doesn't matter what the format of the links are. As long as your client knows that it is supposed to do POST to the "UpVoteLink" or "DownVoteLink" it couldn't care less what the format of the URL is.
Also, if you decide in two weeks that you don't like the URLs you picked, you can change them and no-one will care!
Ok, ok, if you really want a suggestion for an url design, how about
POST http://www.mysite.com/UpVotes?url=http://www.mysite.com/images/1234.jpg
POST http://www.mysite.com/DownVotes?url=http://www.mysite.com/images/1234.jpg
What is cool about this design is that you could vote on images that are not even on your site!
In terms of resources, an image is a thing that has a URI (that is what makes it a resource). Further than that, it has a bunch of properties (size, EXIF data, etc).
When you think of the votes for an image, question if the votes are a resource in themselves.
Chances are, doing a GET on /images/23/votes would return a summary or the list of all votes that the UI would use to display next to the image. Whenever you want to change those votes, the resource you're changing is the votes.
To be restful, the client needs to only understand the media type you've designed, not the URIs or the process to follow to vote.
In your example, you'd define a new format used everywhere on your site. To reformulate yoru example, a GET /images/23/votes would return (in xml but you can reformulate it in json):
<votes>
<link href="/images/23" rel="subject" />
<form action="/images/23/votes" mediatype="application/json">
<submit name="Vote" value="Up">Vote up</submit>
<submit name="Vote" value="Down">Vote down</submit>
</form>
</votes>
The idea behind this format is that you have a universal way to define how the client sends the data to the server and build the document. All the previous examles that have been shown correctly make the URI dependent on the server. I propose that what you send to the server should be defined in forms that the server sent.
So spend more time defining how yoru json client is going to understand how to build json objects for submission based on a general form language, and you'll find that once this is done, you have very low coupling between cient and server, and the server has the flexibility to change all the specifics without breaking your clients.
REST APIs are supposed to represent nouns so I think you've the first part correct: a single image is represented by a single URL (e.g. http://www.mysite.com/images/123123). I'm not sure tacking on /up_vote and /down_vote is the way to go though.
http://www.mysite.com/images/123123 is the object and you want to modify that, not some other URL (unless you were doing http://www.mysite.com/votes/images/123123). I think you should just POST to http://www.mysite.com/images/123123. This makes GET requests inherently non-destructive since it just retrieves the image, preserves a RESTful design, and keeps your URLs clean.
If you're working with Rails, I'd go for this GET-URLs:
http://www.mysite.com/images/123123/up_vote
and
http://www.mysite.com/images/123123/down_vote
1.
Define the actions "up_vote" and "down_vote" in your Images controller and have it increase or decrease the vote value of your image-model-object.
2.
Set the following route in config/routes.rb:
map.resources :images, :member => { :up_vote => :get, :down_vote => :get }
...and you're done (more or less ;-)).
It would seem silly to me to POST a simple response like that wrapped in JSON
Why not something simple like this, you could do it in an AJAX call to make it uber nice..
A GET request formatted as follows
http://www.mysite.com/vote.php?image=123&vote=up
or POST (this example using jQuery)
$.post("http://www.mysite.com/vote.php", {image:"123", vote:"up"});
(Assuming PHP, but whatever applies)
Given:
/images: list of all images
/images/{imageId}: specific image
/feed/{feedId}: potentially huge list of some images (not all of them)
How would you query if a particular feed contains a particular image without downloading the full list? Put another way, how would you check whether a resource state contains a component without downloading the entire state? The first thought that comes to mind is:
Alias /images/{imageId} to /feed/{feedId}/images/{imageId}
Clients would then issue HTTP GET against /feed/{feedId}/images/{id} to check for its existence. The downside I see with this approach is that it forces me to hard-code logic into the client for breaking down an image URI to its proprietary id, something that REST frowns upon. Ideally I should be using the opaque image URI. Another option is:
Issue HTTP GET against /feed/{feedId}?contains={imageURI} to check for existence
but that feels a lot closer to RPC than I'd like. Any ideas?
What's wrong with this?
HEAD /images/id
It's unclear what "feed" means, but assuming it contains resources, it'd be the same:
HEAD /feed/id
It's tricky to say without seeing some examples to provide context.
But you could just have clients call HEAD /feed/images/{imageURI} (assuming that you might need to encode the imageURI). The server would respond with the usual HEAD response, or with a 404 error if the resource doesn't exist. You'd need to code some logic on the server to understand the imageURI.
Then the client either uses the image meta info in the head, or gracefully handles the 404 error and does something else (depending on the application I guess)
There's nothing "un-RESTful" about:
/feed/{feedId}?contains={imageURI}[,{imageURI}]
It returns the subset as specified. The resource, /feed/{feedid}, is a list resource containing a list of images. How is the resource returned with the contains query any different?
The URI is unique, and returns the appropriate state from the application. Can't say anything about the caching semantics of the request, but they're identical to whatever the caching semantics are of the original /feed/{feedid}, it simply a subset.
Finally, there's nothing that says that there even exists a /feed/{feedid}/image/{imageURL}. If you want to work with the sub-resources at that level, then fine, but you're not required to. The list coming back will likely just be a list of direct image URLS, so where's the link describing the /feed/{feedid}/image/{imageURL} relationship? You were going to embed that in the payload, correct?
How about setting up a ImageQuery resource:
# Create a new query from form data where you could constrain results for a given feed.
# May or may not redirect to /image_queries/query_id.
POST /image_queries/
# Optional - view query results containing URIs to query resources.
GET /image_queries/query_id
This video demonstrates the idea using Rails.