Why is POST /accounts/4402278/close correct in the first example? According to the rest of the post, PUT should be a better option. Closing an account seems to me like its updating a resource, not creating one. Also, will calling that url close the account multiple times? POST is not supposed to be idempotent
Because you should never, never, never create your own verbs.
The main reason APIs have become ubiquitous is that we have a shared understanding that follows us from API to API. That includes the concept of verbs, response codes, and how to interact with the nouns/resources. If you add verbs, you're breaking that agreement.
Also, from a technical perspective, some of the internet infrastructure - servers, routers, etc - drop things they don't recognize. Heroku for years was dropping the PATCH verb despite recommending its use for customers' APIs.. and PATCH is in one of the specifications! Imagine what would happen to your custom verb.
In CRUD, delete is rarely a hard delete that removes the data entirely. Best practice in most situations is setting a flag to mark it deleted. Closing an account is the same behavior - you are not purging the data, rather setting it to a closed/deleted/archived state.
You're packing a lot of implied behavior - based on your experiences - into the DELETE verb instead of using the dictionary definition.
If everyone has experience with similar systems, then you're fine. If others have worked with hard delete systems or aren't native English speakers, you're introducing ambiguity and context that isn't necessarily there.
DELETE simply means subsequent calls to GET should not return that object.
So you can call DELETE on an account then your next GET returns a 404 because you deleted it from the database, or you can call DELETE and your next GET returns a 405 because the account has been closed and you're not allowed to call GET on a closed account. Both are totally within the spirit of what DELETE is supposed to accomplish, and I'm sure there are a half dozen other ways to architect it completely within the spirit of DELETE and REST as well.
Because the action is designed to affect the /accounts/4402278 resource. Requiring a PUT here would (semantically) imply that /accounts/4402278 is a container that has various sub-resources.
Also, there is nothing wrong with having an idempotent POST effect. "POST is not supposed to be idempotent" is not the same as "POST should never be idempotent". In this case, the POST acts on a boolean flag; it is harder to make it not idempotent.
I mean, we're running up against one of the problems which metadata systems have to deal with, which is that metadata about an object really "lives" in parallel with that object, and not really "contained within it". But usually you just think of an object as containing all of the metadata we know about it so far, and under that model this makes perfect sense.
To do this in 100% RESTful style you would therefore probably store details about how an account was closed in the /accounts/440278/closed reference, so a PUT is correct, with GET /accounts/440278/closed returning either a 404 for accounts which are not closed or else the metadata about how the account was closed. This could live in parallel with a cache that's on the resource itself describing whether it was closed or not.
So, POST or PUT, with the PUT exposing more of the metadata interface to REST and POST putting more of it in the request bodies themselves.
You may be operating under the misapprehension that "POST" means "CREATE".
It WOULD make a nice set with CRUD operations:
POST - Create
GET - Read
PUT - Update
DELETE - Delete
But that's not actually how the HTTP verbs are defined[1]. Instead, my own mental mapping looks something like this:
GET - read information. Must be idempotent and side-effect free so this one really IS just for reading.
DELETE- delete. Usually pretty obvious except for arguments about whether a "soft delete" (mark as deleted but don't remove from the underlying DB -- like a bank account which can be closed but continues to exist (its account number, for instance, is never freed up).
PUT - update things. Must be idempotent. If you're a stickler for doing things right, this should NOT be use for a "partial update" (where you supply a few fields and everything else is left "as is") but only for a "total update" (where the new thing you sent in replaces whatever was there).
POST - "do it". Means to do the obvious thing (whatever that is for this URL).
PATCH - great idea. Maybe in a few years support for it will be ubiquitous enough that we can actually start using it. If we DID us it, this would be the partial update variant of PUT.
OPTIONS and HEAD - occasionally used at a framework level. You won't ever use it.
I like to think of DELETE as "if the response to the DELETE is 200-level then further GETs to the resource should be at the 400-level." Call those responses to GET requests "2XXing" and "4XXing" respectively.
Similarly a PUT should make sense whether the underlying resource 4XXes or 2XXes, and should in either case make it 2XX with the same response.
Then POST is just a verb which does not share these semantics at all; POST <url> can be done on a URL which either 4XXes or 2XXes, and even if the post succeeds that's no guarantee that the underlying URL will now exist. POST /update-caches for example might not change the HTTP statuses of anything.
You could PUT the entire value of the resource representing account 4402278 (to include owner's name, contact information, history, &c. &c. &c., or you could PUT a custom range to just update the account status, or you could POST just the update. Honestly, if custom ranges worked a bit better, that'd probably be the best option.
Given that they don't, really, I think POST isn't terrible. At least it's not GET!
right, POST is not supposed to be idempotent. However, if you do a PUT on it, you are updating a resource, closing an account may involve more processes than only updating the given resource.
Ok, but why did I say it should be a POST even if POST is not supposed to be idempotent? In my point of view, POST should only be idempotent when creating resources, in this case we're acting in a resource, the /close is a process that happens on that resource (4402278), not a create/update/delete/retrieve. The best way to do it would be a POST in my opinion. Also, if we want to update only a piece of the resource, it should be a PATCH. PUT is supposed to update the whole resource.
Strictly speaking, if you do a PUT on it, you are not updating a resource, you are replacing a resource. I know you basically say the same thing, but "update" is too ambiguous in this case. That is also why PUT is idempotent: it replaces the complete state of the referred resource.
Similarly, POST is not idempotent because it modifies (part of) the resource's state while leaving it otherwise untouched.
I disagree. In the example, account 4402278 is a resource represented at /accounts/4402278; actions on that resource should be performed … on that resource, not on some other resource (e.g. all accounts, at /accounts).
This also gives increased future-proofing, since someday one might have uncloseable accounts; those accounts could still live under /accounts, but would simply have no /close endpoint (as opposed to having /accounts/close, which sometimes works and sometimes doesn't).
DELETE is for deletion only, this request can't have body, so only what we can do is send id in query. When we want to remove multiple resources we either should send multiple requests (what is slow), or combine multiple ids in query (what is ugly and non-idiomatic).
The REST design pattern does require that your URLs are structured in this way, and I don't think that's really open to debate. Whether or not all HTTP APIs need need to be RESTful is a different issue.
What about error representation? Most of the time I find that actually representing errors is the hard part of creating a REST api.
You usually want some representation of each field, plus maybe a generic one. I always have troubles with this. Is there a standard or a proper way to define/do this? I would be very interested.
If you are returning multiple errors of different types, (403 for the forbidden change and 422 for the incorrectly set field) then you are supposed to role it up into a generic 400. Google JSON API for more information about the right way to represent multiple errors at a time.
Other commenters are correct that POST /accounts/4402278/close is not right (and also fairly hilariously contradicted in the next section).
Account status (open, closed, suspended, whatever else) is a property of the account, in the same way that the account owner's name is a property of the account. If you went to all the trouble to represent each account as its own resource, which I assume responds correctly otherwise to GET, POST and PUT requests, why is this one property special enough to get its own endpoint? Why would you not just PUT or PATCH the account to change the "status" property to "closed"?
In my experience, programmers typically break best practices in this way when there is special logic that needs to happen when the property changes. In other words, PUT is fine as long as it's only overlaying new data and not triggering other processes, but closing an account kicks off a whole host of internal processes at the business, so it seemed reasonable to someone to make it a separate endpoint.
This either represents a friction between good API design and what programmers find reasonable ("I have to make a bunch of special things happen, so I'll bundle them into their own function and expose them"), or the API framework isn't flexible enough to supply hooks to insert logic at field-level changes, or both.
The action is "close". The status may or may not be "closed", but that's not what he's doing. Read a little farther. In the brief example JSON for the accounts, there is no "status" property. In fact, there are four actions available on the account:
For the deposit/withdrawal/transfer set, depending on the specifics of how they are actually moving the amounts around, would likely be best served as their own resource. Since a deposit/withdrawal is just a transfer anyways, just having "/transfer" would be likely be good. The connotation here is that the client is creating a transfer on the server. The body for this request could take the relevant account numbers (you need two accounts), and the sign on the amount would denote if the amount is being deposited or withdrawn.
Having the /account/:accountid/transfer is not a terrible idea, but I would avoid it because the transfer resource is able to stand on it's own with making it tied to a single account resource.
The key point though is that 'transfer' is the resource to perform all three of the original deposit/withdrawal/transfer actions.
One way to approach it would be to have consider deposits, withdrawals, and transfers as subresources of a specific account. So you could POST to "/account/4502278/deposits" to create a new deposit, which would then live at a URL such as "/account/4502278/deposits/87162". And instead of separate subresources for all of these different transaction, it could just be one "transaction" subresource.
In the case of closing an account, it depends on what actually happens when you do so, but I would normally have a "status" attribute and use PATCH to update that.
I personally think that's less readable than the original, but I agree that it seems to more closely fit the REST standard.
With the deposit, I don't really understand what's changed here from the original. To me, it just looks like deposit has been pluralised, and everything else is the same:
I would simply PATCH { "status" : "closed" }, as that's how PATCH works in rails.
Regarding the deposits, the difference is that 'deposits' is a collection of deposit resources vs 'deposit' as a verb. I think in this case it makes a lot of sense to do it that way, as you can then GET /account/12345/deposits and see all the deposits ever made.
OK, so the original one used a verb at the end of the URL, whereas it should have used a noun. In practice, this wil often means that the URL is almost identical - here for example, it will be:
POST /accounts/12345/deposits
instead of:
POST /accounts/12345/deposit
Perhaps your example would be more obvious if it had different terms, where the noun and verb were more distinct - maybe 'game' and 'play'.
Even then, from experience, you end up with some horrible URLs just so it's RESTful. While I try to follow REST, I balance it against making the API readable by human beings.
> you end up with some horrible URLs just so it's RESTful
No, you don't. RESTful applications use URLs as opaque identifiers, and communicate all information via resource representations. Communicating information via resource identifiers is decidedly not-RESTful, so any particular URL structure chosen to communicate specific information in the URL is, ipso facto, not RESTful.
I'll give a specific specific example from my own experience of building an intranet. I had documents that people could print.
POST /documents/12345/print
To me, while this was not RESTful, it was the most readable way I came up with. When I asked somebody who had more experience with REST than me, he suggested I build the URL like this:
POST /users/123/print-jobs
url=/documents/12345
Doing this, I would have to add validation to make sure it was a printable URL. Also, there was no corresponding GET request, so it seemed pointless. And it made the code weirder, as the 'print' action would be separated from the rest of the actions on documents.
So how would you do it instead? Or you would do it the same way, and just not worry about the problem?
It seems that consensus on how to do REST breaks down when you have custom verbs.
And you say there's not corresponding GET request, but maybe there should be? Perhaps you'd want to list print jobs, get the status of a particular job, and maybe cancel it?
There are lots of aesthetic preferences like this about URLs, but calling them issues of "RESTful"-ness is just plain wrong. REST not only does not prescribe an approach to structuring URLs, it specifies a role for identifiers (URLs in the usual case of HTTP) which makes their construction irrelevant, and worrying about it contrary to the central concept of REST.
> When I asked somebody who had more experience with REST than me, he suggested I build the URL like this:
> POST /users/123/print-jobs
What was the logic: neither version is more RESTful, even if you ignore the role of URLs in REST and adopt what seems to be the usual definition of RESTful URLs used by people who use any coherent definition: the URL represents some logical hierarchy of resources.
In this case, your original "POST /documents/12345/print" implies a collection of print requests (I'd probably call it "prints" or "print-requests" rather than "print", but that's a minor quibble) pertaining to a particular document, to which a new request is added when the user wants to print the document. Sounds perfectly sensible.
The alternative offered suggests a list of print jobs belonging to a user, which is also sensible. But there is no strong reason to prefer one over the other.
And if you were actually RESTful, it wouldn't matter, because your resource representation for the print job/request would actually include both the URL and the user. (And maybe status and other information.)
Both your original (with no representation) and the alternative (with the user in the URL and the document in the resource representation) aren't RESTful because they have information that logically belongs to the resource in the URL and out-of-band context rather as part of the resource. But that's not a matter of URL construction, its fine for a URL scheme to correspond to information about the resources, as long as the information is actually still part of the resource.
> It seems that consensus on how to do REST breaks down when you have custom verbs.
If you have real custom verbs, then you may need to extend HTTP's set of methods (it wouldn't be the first time that has been necessary -- PATCH isn't a base HTTP/1.1 method.)
But there is a difference between a generic action on resources of the type represented by an HTTP method and an action-in-the-domain, and it sounds in your case "print" is an action in the domain, which is an event which should have a corresponding noun -- and thus a resource representation -- in the model. But while URLs are inherently hierarchical, real relationships in real domains are often webs, and so there's not one true way to map it into a hierarchy.
Yes, it should have been a noun, because a URL is a Uniform Resource Locator, and a verb is not a resource. HTTP methods are for verbs. Sometimes this can lead to strange URLs, but applying the noun/verb rule consistently does simplify things.
One thing that helps is not nesting resources too deep, to avoid having really long URLS.
Ah, and therein lies the classic problem. Nobody is doing REST "right," but I'm yet to see anyone point to a de-facto example.
Fielding's dissertation isn't a spec, which pretty much means everyone can come up with their own little slice of how it should be done and then say they're doing it right.
The true rule of REST: whoever is blogging/commenting about it at the time is doing REST right, all others are confused and incorrect.
There is a lot ceremony with "REST". Considering REST is architectural pattern and not even an HTTP-specific one I find it very suspect whenever someone says REST API's must use particular HTTP methods or structure URL's in a specific way.
Conventionally that is true and it certainly reduces friction in your API by adhering to common conventions.
What HTTP methods should be callable on those URL endpoints? Presumably POST should trigger the action it is not idempotent, but what content should we be POSTing?
If "status" is just a property on the accounts resource and doesn't have further meaning, I would tend to agree with you.
If "close" is an action or activity that acts upon an accounts resource, then his approach makes sense.
Since the context is an account that we "need to close," I would assume the author is talking about something more complex than a database field. It's probably a workflow that initiates other actions and workflows, maybe even requiring additional review. I'd want to see/understand the requirements more fully before I started down either path.
REST is just a representation of the underlying data. What does it matter if "status" is a database field or not?
More specifically, it shouldn't have to matter to consumers. Because of the way REST and HTTP work, clients intuitively understand retrieving and modifying resources (via GET, POST, PUT, PATCH and DELETE). But they don't understand interacting with special-purpose endpoints (POST always implies "make a new thing" so it's weird in this context).
Your consumers should not have to learn weird idiosyncrasies in your API because you let your data model bleed into the interface.
No, REST and HTTP are implementation details of the technology.
An API is a representation of business workflows and processes. The more accurately you describe or map those to the real world processes, the better your and your consumers' understanding will be.
What if 'status' isn't a column in the account database? What if closed_accounts is a join table or something else? Then wouldn't adding a /close route be hiding idiosyncrasies in your data model, not the other way around?
It seems like we spend a lot of time designing object relations that map a domain, but maybe not so much mapping domain-specific actions.
Or would you consider all of the above bad practice?
> What if 'status' isn't a column in the account database? What if closed_accounts is a join table or something else? Then wouldn't adding a /close route be hiding idiosyncrasies in your data model, not the other way around?
So what? REST is an API [0]. It's the public interface you expose. What you do behind the scene in your data model is, or should be, irrelevant to the API. Sometimes you'll map your data model practically one-to-one to the REST API, but there are times when I do significant logic in controllers before mapping the result of that logic to a resource. As long as the API is resource oriented and follows good REST practices, it's all good.
0. OK, REST is one way to expose an API, since the wording I used is not amenable to some people.
This doesn't really matter. What you'll end up exposing via your API is your use case! Does your system allow for an account to be closed? Awesome! So let the client know how to do it.
> If "close" is an action or activity that acts upon an accounts resource, then his approach makes sense.
Right. Particularly, if a closure request is a thing that has its own status, identity, and associated data elements, which one might wish to examine and interact with (and, while its possible that such interaction might not be possible for external users with privileges only on their own accounts, it might well be possible for internal administrative users), then it makes sense as a resource, rather than a data element buried in representations of another resource.
Without full behavioral requirements and domain model, you really don't know what makes the most sense.
Certainly, though I am skeptical of that theory being true in the author's actual case. Because if it was, I would think the plurality in the URI would be consistent, and instead of
> Other commenters are correct that POST /accounts/4402278/close is not right (and also fairly hilariously contradicted in the next section).
Of course its right. Read the POST spec. POST is for processing data. Its up to the server to what is processed how. If the POST is for closing a bank account, it is valid. I think, my bank would need verify a lot of things before I can close my account with a single click. So the operation cannot be idempotent and thus requires a POST.
I mean, if you are fine with setting just a flag in your bank, you can be fine with a PUT. But I will not become a customer of your bank.
Just because a flag can be set with PUT doesn't mean the server has to accept it in all circumstances. Maybe there are preconditions elsewhere that must be met first, or maybe only someone with sufficient access credentials can set the flag. This plays pretty well with PUT.
Again, this is the interface to a complex data model, and I would be wary of using a bank that dumped all of its security and process controls into one endpoint's controller.
Also - closing an account is an inherently idempotent operation, no? It can only be closed once. If I request that a closed account be closed again, it stays closed.
A PUT means that the server accepts the payload as is. Yes, it can be restricted as is. But the request must be free of side effects. The server should not even validate the payload. And that also means that your request should not trigger stuff that cannot be triggered again. If your model _only_ relies on that flag, than the PUT is fine. But when I close a bank account, usually I trigger stuff on the server side (including validation). So a POST should be required.
The REST-inspired API that requires 10,000 API calls to do something that could be done in 1. This is a disaster from a simple performance perspective.
There is no simple way to build transactions on top of REST so if you need to do something that involves updating more than one data record you really are best off updating them all in one API call.
When you are writing a web front end this is all the more acute because choreographing a complex interaction with a server is a PITA with asynchronous communications.
If Fielding's thesis went in the trash and got replaced with "one click, one API call, update the UI" we'd hear a lot less carping about how awful the web platform is.
Careful reading of the http spec is a road to hell anyway because 80% of it is dark corners that aren't really used or implemented.
First and foremost an API which services it's clients need well is a good API.
There are many people arguing things that aren't REST (e.g. pretty urls, media type based versioning) in the name of REST, and there are also (fewer) people trying to talk about things that are REST (e.g. HATEOAS) in the name of REST. This makes it (understandably) easy to blame REST due to the large amount of confusion.
In fact, I did a Bad Thing in my other comment by pointing out that REST-as-widely-understood isn't REST as all the mature individuals who are interested in REST APIs have taken to referring to them as Hypermedia APIs in order to avoid having to rehash the differences and kick off a flame war. I wouldn't have done it if OP hadn't mentioned HATEOAS.
This can be resolved somewhat with a SOA or microservices, where one call from a client triggers multiple calls from one service to another. Sure there's overhead (which can be mitigated in a shared data center) but I think this is at least balanced out by modularity.
It's very different "inside the data center" or "inside the cloud" from "services provided to the network edge".
If you want: (i) any chance of all at building a system that works and (ii) happy users, you should codesign the client, protocol and client-facing server if you are providing services to the edge (Web, IoT, etc.)
Inside the data center you can do something else.
The key thing is that REST principles are orthogonal to "a good API", "a successful project", etc.
How I've addressed the transaction issue in the past is to expose it specifically as a transaction. For instance, POST /posts/transactions would contain a list of transformations to /posts and will receive a transaction id as a response. The id can be polled for success (/posts/transactions/:uuid) or results can be pushed to a browser. It is more work to be able to roll that back if one fails, but that's a coding issue not so much an API one.
I'd love some feedback on this approach. How do other people allow efficient mass changes?
This type of approach can be great. It is parallel to the idea to the "business is an exchange of documents", and the documents represent "transactions" in the sense of "an invoice", "a packing slip", "a deposit slip", "an order", etc.
Imagine you want to implement a vacation-booking service
as part of an enterprise system. The first step is to
remove any reference to computers or modern technology.
This will allow you to concentrate on the business
objectives of the service, without getting sidetracked by
technological considerations. In other words, it enables
you to separate the “what” from the “how.”
How I've addressed the transaction issue in the past is to expose it specifically as a transaction. For instance, POST /posts/transactions would contain a list of transformations to /posts and will receive a transaction id as a response. The id can be polled for success (/posts/transactions/:uuid) or results can be pushed to a browser. It is more work to be able to roll that back if one fails, but that's a coding issue not so much an API one.
I'd love some feedback on this approach. How do other people allow efficient mass changes?
You might want to check out GraphQL: http://graphql.org/. One of its killer features is the ability for clients to specify exactly the data it needs and obtain it in a single request/response.
Where the server goes from requiring multiple requests to a single request without changing the clients whatsoever. So, I feel you're comment is on the right path (less http requests are better for the client and server), but might not realize that things can be optimized further.
> The REST-inspired API that requires 10,000 API calls to do something that could be done in 1.
That's not a REST-inspired API. (It might be an API inspired by the kind of naïve mapping of HTTP verbs onto the base tables of a normalized RDBMS schema suggested to people who don't understand DB design, REST, or application design by certain popular web frameworks default "REST" approach, but that's not REST's fault.)
If your application needs so many objects at once, yes, then you probably need something else. Or you just abstract it away through some POST interface alongside with the 10k resources.
> There is no simple way to build transactions on top of REST so if you need to do something that involves updating more than one data record you really are best off updating them all in one API call.
Yes, you can do that. I mean, if your really need to do that. Otherwise, your client should check the server resources for whether they represent to correct data. And you can always implement something like implicit transactions, where you create a transaction resource and append data to it or create new resources in a certain context.
EDIT: BTW. You don't have to do everything in REST. You can always "upgrade your protocol". So having a link myrpc://myserver:port is very valid.
> If Fielding's thesis went in the trash and got replaced with "one click, one API call, update the UI" we'd hear a lot less carping about how awful the web platform is.
Then we would end up building clients that always implement 100% of your RPC API. And we would not be able to move resources around. Implementing clients and servers would become an all-or-nothing thing, which really bad.
> Careful reading of the http spec is a road to hell anyway because 80% of it is dark corners that aren't really used or implemented.
By that you mean, that YOU have only used 20% of the spec so far? So why is it bad in general? I did not see these dark corners.
>The REST-inspired API that requires 10,000 API calls to do something that could be done in 1.
That should be a non-issue. Consider search queries. They are a simple GET request that returns a collection of many results in one response. You can do the same, you just need to define collection resources or collection semantics for existing resources.
The only challenge is handling partial failures, like when you POST 1000 things, and 22 of them fail validation. So each thing needs a status code in the response and some unique ID so that you can find its status code in the response. (That is separate from the overall request status code.) I have the client send a UUID and the server responds with a collection keyed by that UUID so that it's easy to find, for each thing that you sent, what happened.
HATEOAS is much less important for programmatic APIs. You either hard-code knowledge of the URL scheme in the API client, or you hard-code knowledge of the payload schema in the API client. There isn't a huge amount of difference here IMO, especially if you have some kind of versioning mechanism in your URL routing.
Hard-coding URL scheme permits more pipelining and concurrency in the client. Embedding URLs in payloads forces sequencing. This alone can make the templated URL scheme a win for interactivity.
The dream is that your client can dynamically update itself or seek out ways to process new, incomprehensible things* that it encounters while traversing a rest request (see "Code-on-demand in REST literature).
If you think this is possible in the wild or not is another matter.
*each nugget of data can be versioned and meaninged independently
That, and the semantic web, and pluggable components from different vendors using standardized interfaces, and other architecture astronaut ideas of software composition. Any day now.
Sounds a bit like UML+Rational, visual programming, and every other one size fits all panacea, to be honest.
The whole notion of HTTP as an application data transport is terrible. We've hacked at it to create a great number of wonderful web based apps, but I can't help thinking there's a better way.
If your implementation can rely on Hypertext, you can build it different. You can build a state machine where parts of your application are "componentised" without knowing the context of a resource.
My critique with POST /login is that it's a verb, not a noun. Whether the session is stored on the server or the client is the same regardless of the verbiage. In both cases a client-side session is a bit of semantic abuse but HTTP does not have verbs for creating resources on the client.
I think a JWT token represents a session, since it can expire and be disposed when logging out.
Resource verbs in POST URLs are almost always a bad idea. You POST a command via the body to the resource and should expect a result in turn.
Though I do understand the idea that URLs with verbs allow the API to be self describing via links, I think it's a bit naive (and verbose) to think a user will get all the info they need from hitting the API. RAML and its variants do a great job of conveying this information.
The initial and fundamental problem is a misapplication of REST. To take advantage of REST, you need some form of hypertext, not raw JSON APIs.
Additionally, on a practical level, you need the end points to be relatively coarse grained, which can be achieved if you design the API for your UI use case, rather than general data access:
The issue with HATEOAS is a little overstated. You don't need a full AI with independent agency. What you need is a clear media type that corresponds to some domain concepts that both the client and server agree on. The client and server don't even have to totally agree, as long as there is some overlap. The server may provide functions that the client doesn't care about, and the client may care about functions that the server doesn't provide (but are provided by another server). Failure cases are:
- media types totally disjoint (this wouldn't work for a human either, since the server couldn't do anything that the user wants to do with the resource)
- media type misunderstandings - there is overlap in the functions, but what the client intends and what the server does are two different things, because their concept of that domain function is different (that would also be the same problem for a human)
- media types undefined - everyone is just passing random unlabeled JSON globs, so any domain concepts have to be hardcoded equivalent on both sides (this is one case where a human might be able to intuitively guess the right thing, but only if their mental model happens to match the server's model)
The solution is to document the domain concepts of the media types that fit your domain from both perspectives, find the places that don't overlap or match in name but not semantics, and decide what to do about them.
The content of the blog post is okay, but the premise is flawed. Most of us have reluctantly accepted the abuse of terminology that happens when everyone calls these APIs 'RESTful' -- but they're not. They're inspired by REST, but cargo-cult took the easiest-to-implement pieces all the while pretending to stand on some moral high-ground about not being openly RPC because 'RESTful is the right way'.
This results in a worst-of-both-worlds situation, where most of these API don't have custom mediatypes that I can Accept: header for, don't have custom link relations one can programmatically traverse, and some poor person had to contort their data model to come up with plausibly 'resourcey' objects to make HTTP calls against, all the while losing simple reassurance you would've gotten from an uncool RPC endpoint.
HATEOAS is critical to REST -- it being nothing more than a terribly obtuse rendition of the ideas behind how the web (but more importantly, the semantic web) works. This isn't the usual rant complaining about how HATEOAS is most implementers' afterthought; this is the rant about how HATEOAS is the entire damn point; without it you're just squirting JSON on the wire and using HTTP as a transport because it doesn't get blocked on a middlebox.
And now that we've retrofitted schemas into JSON, and moved half our APIs to use CORS/CSP-needing PUT/DELETE methods, and require OAuth for more than half of the requests, the original advantages of this scheme are entirely gone -- you can no longer just muck around in some half-baked javascript, parse out a single field, discard the rest and surface it in a 'web 2.0 mashup'. And when the vendor supplies the SDK anyway (even in Javascript), the exact form the messages take on the wire is entirely irrelevant. I hope the new wave of RPC (helped by the bi-directional multiplexing transport protocol inexplicably known as HTTP/2) becomes the new fad and kills this awful fumbling with cargo-cult fake-REST once and for all.
Also an absolutely fantastic article from Jacob Kablan-Moss, one of the three original founder of Django on REST anti-patterns (not Django specific at all):
I'm currently working on a Database Proxy API Project I cooked up (essentially creating a way I so we could query our different MySQL and Oracle databases using a RESTful API) and it makes sense for me (from the RESTful perspective) to use a GET request to send the query.
However, I know that the request body (a JSON string or a JSON array that could contain one or more queries, along with one or more different decryption keys allowing the server-side to retrieve the connection details that are encrypted on the server) could be quite long (we have some massive SQL queries that get used elsewhere and could potentially be used with this new API once it's available) and I also didn't like the idea of some of the more sensitive information being in the URL, so I decided to implement it as a POST request instead of a GET.
It feels wrong (again from the RESTful perspective since I'd like my API to be fully RESTful) but from a user point of view I know I'm making the right decision.
If anybody has better ideas though, please let me know!
Question 1#: Is there a reason PATCH is used (or even needed?), instead of PUT to update records?
(Create, Read, Update & Delete => POST, GET, PUT & DELETE) seem like a natural fit, why complicate things?
Question 2#: What is the consensus around custom verbs?
While I've never gone down this route, a few times I have been tempted.
Re #1, PATCH is useful because PUT requires that you replace the entire resource even if you just want to change one little thing and to do that safely, you need the now-current-latest representation of the full resource. A common case is looking at resource A, which is related to resource B (and therefore you have B's ID, but not its full data), you can patch resource B specifying just its ID and the change that you want to make without having to fetch the whole thing first.
Also, PUT can conflict (or overwrite changes) if something else has altered the resource since you fetched it. PATCH is more granular, so you can make it a lot less likely to conflict or overwrite (in addition to being more efficient).
Re #2, I don't know about consensus, but usually when I think that I could use a custom verb, some more thought reveals that I could instead use a standard verb on another resource that I haven't spec'd out yet (like the comments about having a 'transaction' resource representing details of a transaction instead of a 'transfer' verb).
Hypothetically, with PUT, whatever you send to the server should now comprise the entirety of that resource. So if all you PUT is a last name to /people/4335, then all of a sudden person 4335 consists of nothing more than a last name alone in the void, and not even blank fields beside. With PATCH, on the other hand, you can confidently send the value of just one updated field, without your users worrying about their metaphysical well-being.
132 comments
[ 3.3 ms ] story [ 206 ms ] threadThe main reason APIs have become ubiquitous is that we have a shared understanding that follows us from API to API. That includes the concept of verbs, response codes, and how to interact with the nouns/resources. If you add verbs, you're breaking that agreement.
Also, from a technical perspective, some of the internet infrastructure - servers, routers, etc - drop things they don't recognize. Heroku for years was dropping the PATCH verb despite recommending its use for customers' APIs.. and PATCH is in one of the specifications! Imagine what would happen to your custom verb.
Don't create your own. Stick to the standards.
If everyone has experience with similar systems, then you're fine. If others have worked with hard delete systems or aren't native English speakers, you're introducing ambiguity and context that isn't necessarily there.
So you can call DELETE on an account then your next GET returns a 404 because you deleted it from the database, or you can call DELETE and your next GET returns a 405 because the account has been closed and you're not allowed to call GET on a closed account. Both are totally within the spirit of what DELETE is supposed to accomplish, and I'm sure there are a half dozen other ways to architect it completely within the spirit of DELETE and REST as well.
Also, there is nothing wrong with having an idempotent POST effect. "POST is not supposed to be idempotent" is not the same as "POST should never be idempotent". In this case, the POST acts on a boolean flag; it is harder to make it not idempotent.
To do this in 100% RESTful style you would therefore probably store details about how an account was closed in the /accounts/440278/closed reference, so a PUT is correct, with GET /accounts/440278/closed returning either a 404 for accounts which are not closed or else the metadata about how the account was closed. This could live in parallel with a cache that's on the resource itself describing whether it was closed or not.
So, POST or PUT, with the PUT exposing more of the metadata interface to REST and POST putting more of it in the request bodies themselves.
It WOULD make a nice set with CRUD operations:
POST - Create
GET - Read
PUT - Update
DELETE - Delete
But that's not actually how the HTTP verbs are defined[1]. Instead, my own mental mapping looks something like this:
GET - read information. Must be idempotent and side-effect free so this one really IS just for reading.
DELETE- delete. Usually pretty obvious except for arguments about whether a "soft delete" (mark as deleted but don't remove from the underlying DB -- like a bank account which can be closed but continues to exist (its account number, for instance, is never freed up).
PUT - update things. Must be idempotent. If you're a stickler for doing things right, this should NOT be use for a "partial update" (where you supply a few fields and everything else is left "as is") but only for a "total update" (where the new thing you sent in replaces whatever was there).
POST - "do it". Means to do the obvious thing (whatever that is for this URL).
PATCH - great idea. Maybe in a few years support for it will be ubiquitous enough that we can actually start using it. If we DID us it, this would be the partial update variant of PUT.
OPTIONS and HEAD - occasionally used at a framework level. You won't ever use it.
everything else - don't use it.
[1] - https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
Similarly a PUT should make sense whether the underlying resource 4XXes or 2XXes, and should in either case make it 2XX with the same response.
Then POST is just a verb which does not share these semantics at all; POST <url> can be done on a URL which either 4XXes or 2XXes, and even if the post succeeds that's no guarantee that the underlying URL will now exist. POST /update-caches for example might not change the HTTP statuses of anything.
Given that they don't, really, I think POST isn't terrible. At least it's not GET!
Similarly, POST is not idempotent because it modifies (part of) the resource's state while leaving it otherwise untouched.
bullshit. POST should not contain all details in URL. POST can have body and each query should not be unique. Stopped reading after that.
This also gives increased future-proofing, since someday one might have uncloseable accounts; those accounts could still live under /accounts, but would simply have no /close endpoint (as opposed to having /accounts/close, which sometimes works and sometimes doesn't).
PATCH /accounts/4402278
with parameter ACTIVE: 0
"If the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period."
If you're doing REST as-in JSON-over-http + a docs page, knock yourself out.
edit: Also see: http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post
You usually want some representation of each field, plus maybe a generic one. I always have troubles with this. Is there a standard or a proper way to define/do this? I would be very interested.
On an old project our backend team agreed to support PATCH, but they wanted it based on this RFC: https://tools.ietf.org/html/rfc6902
The front end team wanted something more like this (before it was ensconced in an RFC): https://tools.ietf.org/html/rfc7386
To me, RFC7386 more accurately represents the idea of just sending the updated content.
Account status (open, closed, suspended, whatever else) is a property of the account, in the same way that the account owner's name is a property of the account. If you went to all the trouble to represent each account as its own resource, which I assume responds correctly otherwise to GET, POST and PUT requests, why is this one property special enough to get its own endpoint? Why would you not just PUT or PATCH the account to change the "status" property to "closed"?
In my experience, programmers typically break best practices in this way when there is special logic that needs to happen when the property changes. In other words, PUT is fine as long as it's only overlaying new data and not triggering other processes, but closing an account kicks off a whole host of internal processes at the business, so it seemed reasonable to someone to make it a separate endpoint.
This either represents a friction between good API design and what programmers find reasonable ("I have to make a bunch of special things happen, so I'll bundle them into their own function and expose them"), or the API framework isn't flexible enough to supply hooks to insert logic at field-level changes, or both.
This isn't how REST is supposed to work
The key point though is that 'transfer' is the resource to perform all three of the original deposit/withdrawal/transfer actions.
In the case of closing an account, it depends on what actually happens when you do so, but I would normally have a "status" attribute and use PATCH to update that.
With the deposit, I don't really understand what's changed here from the original. To me, it just looks like deposit has been pluralised, and everything else is the same:
Regarding the deposits, the difference is that 'deposits' is a collection of deposit resources vs 'deposit' as a verb. I think in this case it makes a lot of sense to do it that way, as you can then GET /account/12345/deposits and see all the deposits ever made.
Even then, from experience, you end up with some horrible URLs just so it's RESTful. While I try to follow REST, I balance it against making the API readable by human beings.
No, you don't. RESTful applications use URLs as opaque identifiers, and communicate all information via resource representations. Communicating information via resource identifiers is decidedly not-RESTful, so any particular URL structure chosen to communicate specific information in the URL is, ipso facto, not RESTful.
So how would you do it instead? Or you would do it the same way, and just not worry about the problem?
It seems that consensus on how to do REST breaks down when you have custom verbs.
I don't have a print queue because nobody has ever asked for one or shown the need for one.
> When I asked somebody who had more experience with REST than me, he suggested I build the URL like this:
> POST /users/123/print-jobs
What was the logic: neither version is more RESTful, even if you ignore the role of URLs in REST and adopt what seems to be the usual definition of RESTful URLs used by people who use any coherent definition: the URL represents some logical hierarchy of resources.
In this case, your original "POST /documents/12345/print" implies a collection of print requests (I'd probably call it "prints" or "print-requests" rather than "print", but that's a minor quibble) pertaining to a particular document, to which a new request is added when the user wants to print the document. Sounds perfectly sensible.
The alternative offered suggests a list of print jobs belonging to a user, which is also sensible. But there is no strong reason to prefer one over the other.
And if you were actually RESTful, it wouldn't matter, because your resource representation for the print job/request would actually include both the URL and the user. (And maybe status and other information.)
Both your original (with no representation) and the alternative (with the user in the URL and the document in the resource representation) aren't RESTful because they have information that logically belongs to the resource in the URL and out-of-band context rather as part of the resource. But that's not a matter of URL construction, its fine for a URL scheme to correspond to information about the resources, as long as the information is actually still part of the resource.
> It seems that consensus on how to do REST breaks down when you have custom verbs.
If you have real custom verbs, then you may need to extend HTTP's set of methods (it wouldn't be the first time that has been necessary -- PATCH isn't a base HTTP/1.1 method.)
But there is a difference between a generic action on resources of the type represented by an HTTP method and an action-in-the-domain, and it sounds in your case "print" is an action in the domain, which is an event which should have a corresponding noun -- and thus a resource representation -- in the model. But while URLs are inherently hierarchical, real relationships in real domains are often webs, and so there's not one true way to map it into a hierarchy.
One thing that helps is not nesting resources too deep, to avoid having really long URLS.
Fielding's dissertation isn't a spec, which pretty much means everyone can come up with their own little slice of how it should be done and then say they're doing it right.
The true rule of REST: whoever is blogging/commenting about it at the time is doing REST right, all others are confused and incorrect.
Conventionally that is true and it certainly reduces friction in your API by adhering to common conventions.
If "status" is just a property on the accounts resource and doesn't have further meaning, I would tend to agree with you.
If "close" is an action or activity that acts upon an accounts resource, then his approach makes sense.
Since the context is an account that we "need to close," I would assume the author is talking about something more complex than a database field. It's probably a workflow that initiates other actions and workflows, maybe even requiring additional review. I'd want to see/understand the requirements more fully before I started down either path.
More specifically, it shouldn't have to matter to consumers. Because of the way REST and HTTP work, clients intuitively understand retrieving and modifying resources (via GET, POST, PUT, PATCH and DELETE). But they don't understand interacting with special-purpose endpoints (POST always implies "make a new thing" so it's weird in this context).
Your consumers should not have to learn weird idiosyncrasies in your API because you let your data model bleed into the interface.
An API is a representation of business workflows and processes. The more accurately you describe or map those to the real world processes, the better your and your consumers' understanding will be.
It seems like we spend a lot of time designing object relations that map a domain, but maybe not so much mapping domain-specific actions.
Or would you consider all of the above bad practice?
So what? REST is an API [0]. It's the public interface you expose. What you do behind the scene in your data model is, or should be, irrelevant to the API. Sometimes you'll map your data model practically one-to-one to the REST API, but there are times when I do significant logic in controllers before mapping the result of that logic to a resource. As long as the API is resource oriented and follows good REST practices, it's all good.
0. OK, REST is one way to expose an API, since the wording I used is not amenable to some people.
No, REST is an architectural style.
For APIs. But yes, we can argue semantics now. My point is, REST is a way to expose an API.
If you're speaking HTML it can be as simple as:
Or even: If you're speaking Siren (https://github.com/kevinswiber/siren):Right. Particularly, if a closure request is a thing that has its own status, identity, and associated data elements, which one might wish to examine and interact with (and, while its possible that such interaction might not be possible for external users with privileges only on their own accounts, it might well be possible for internal administrative users), then it makes sense as a resource, rather than a data element buried in representations of another resource.
Without full behavioral requirements and domain model, you really don't know what makes the most sense.
POST /accounts/4402278/close
You would have something like
POST /accounts/4402278/closures
Of course its right. Read the POST spec. POST is for processing data. Its up to the server to what is processed how. If the POST is for closing a bank account, it is valid. I think, my bank would need verify a lot of things before I can close my account with a single click. So the operation cannot be idempotent and thus requires a POST.
I mean, if you are fine with setting just a flag in your bank, you can be fine with a PUT. But I will not become a customer of your bank.
Again, this is the interface to a complex data model, and I would be wary of using a bank that dumped all of its security and process controls into one endpoint's controller.
Also - closing an account is an inherently idempotent operation, no? It can only be closed once. If I request that a closed account be closed again, it stays closed.
The REST-inspired API that requires 10,000 API calls to do something that could be done in 1. This is a disaster from a simple performance perspective.
There is no simple way to build transactions on top of REST so if you need to do something that involves updating more than one data record you really are best off updating them all in one API call.
When you are writing a web front end this is all the more acute because choreographing a complex interaction with a server is a PITA with asynchronous communications.
If Fielding's thesis went in the trash and got replaced with "one click, one API call, update the UI" we'd hear a lot less carping about how awful the web platform is.
Careful reading of the http spec is a road to hell anyway because 80% of it is dark corners that aren't really used or implemented.
And what you describe nothing to do with REST, it's to do with overly fine-grained API design, without taking your users into account.
There are many people arguing things that aren't REST (e.g. pretty urls, media type based versioning) in the name of REST, and there are also (fewer) people trying to talk about things that are REST (e.g. HATEOAS) in the name of REST. This makes it (understandably) easy to blame REST due to the large amount of confusion.
In fact, I did a Bad Thing in my other comment by pointing out that REST-as-widely-understood isn't REST as all the mature individuals who are interested in REST APIs have taken to referring to them as Hypermedia APIs in order to avoid having to rehash the differences and kick off a flame war. I wouldn't have done it if OP hadn't mentioned HATEOAS.
If you want: (i) any chance of all at building a system that works and (ii) happy users, you should codesign the client, protocol and client-facing server if you are providing services to the edge (Web, IoT, etc.)
Inside the data center you can do something else.
The key thing is that REST principles are orthogonal to "a good API", "a successful project", etc.
I'd love some feedback on this approach. How do other people allow efficient mass changes?
Did you just make up this phrase? It's really good!
[1]: https://dannorth.net/classic-soa/
https://www.kuali.org/kfs
and that was the thinking behind how that system was built. Also this is the paradigm behind
http://www.enterpriseintegrationpatterns.com/
and it must predate the computer.
I'd love some feedback on this approach. How do other people allow efficient mass changes?
[0]: http://www.datomic.com/videos.html
Where the server goes from requiring multiple requests to a single request without changing the clients whatsoever. So, I feel you're comment is on the right path (less http requests are better for the client and server), but might not realize that things can be optimized further.
Here's what you need to do to copy a file to different folder in C#:
To be fair it's just 2 API calls but rather verbose. And that's assuming all the calls workThat's not a REST-inspired API. (It might be an API inspired by the kind of naïve mapping of HTTP verbs onto the base tables of a normalized RDBMS schema suggested to people who don't understand DB design, REST, or application design by certain popular web frameworks default "REST" approach, but that's not REST's fault.)
> There is no simple way to build transactions on top of REST so if you need to do something that involves updating more than one data record you really are best off updating them all in one API call.
Yes, you can do that. I mean, if your really need to do that. Otherwise, your client should check the server resources for whether they represent to correct data. And you can always implement something like implicit transactions, where you create a transaction resource and append data to it or create new resources in a certain context.
EDIT: BTW. You don't have to do everything in REST. You can always "upgrade your protocol". So having a link myrpc://myserver:port is very valid.
> If Fielding's thesis went in the trash and got replaced with "one click, one API call, update the UI" we'd hear a lot less carping about how awful the web platform is.
Then we would end up building clients that always implement 100% of your RPC API. And we would not be able to move resources around. Implementing clients and servers would become an all-or-nothing thing, which really bad.
> Careful reading of the http spec is a road to hell anyway because 80% of it is dark corners that aren't really used or implemented.
By that you mean, that YOU have only used 20% of the spec so far? So why is it bad in general? I did not see these dark corners.
That should be a non-issue. Consider search queries. They are a simple GET request that returns a collection of many results in one response. You can do the same, you just need to define collection resources or collection semantics for existing resources.
The only challenge is handling partial failures, like when you POST 1000 things, and 22 of them fail validation. So each thing needs a status code in the response and some unique ID so that you can find its status code in the response. (That is separate from the overall request status code.) I have the client send a UUID and the server responds with a collection keyed by that UUID so that it's easy to find, for each thing that you sent, what happened.
Hard-coding URL scheme permits more pipelining and concurrency in the client. Embedding URLs in payloads forces sequencing. This alone can make the templated URL scheme a win for interactivity.
If you think this is possible in the wild or not is another matter.
*each nugget of data can be versioned and meaninged independently
The whole notion of HTTP as an application data transport is terrible. We've hacked at it to create a great number of wonderful web based apps, but I can't help thinking there's a better way.
You should use POST /session instead.
I think a JWT token represents a session, since it can expire and be disposed when logging out.
Though I do understand the idea that URLs with verbs allow the API to be self describing via links, I think it's a bit naive (and verbose) to think a user will get all the info they need from hitting the API. RAML and its variants do a great job of conveying this information.
Additionally, on a practical level, you need the end points to be relatively coarse grained, which can be achieved if you design the API for your UI use case, rather than general data access:
http://intercoolerjs.org/2016/01/18/rescuing-rest.html
Shoehorning HATEOS in JSON APIs is a category error:
http://intercoolerjs.org/2016/05/08/hatoeas-is-for-humans.ht...
Long story short, REST: you are all doing it completely wrong.
- media types totally disjoint (this wouldn't work for a human either, since the server couldn't do anything that the user wants to do with the resource)
- media type misunderstandings - there is overlap in the functions, but what the client intends and what the server does are two different things, because their concept of that domain function is different (that would also be the same problem for a human)
- media types undefined - everyone is just passing random unlabeled JSON globs, so any domain concepts have to be hardcoded equivalent on both sides (this is one case where a human might be able to intuitively guess the right thing, but only if their mental model happens to match the server's model)
The solution is to document the domain concepts of the media types that fit your domain from both perspectives, find the places that don't overlap or match in name but not semantics, and decide what to do about them.
This results in a worst-of-both-worlds situation, where most of these API don't have custom mediatypes that I can Accept: header for, don't have custom link relations one can programmatically traverse, and some poor person had to contort their data model to come up with plausibly 'resourcey' objects to make HTTP calls against, all the while losing simple reassurance you would've gotten from an uncool RPC endpoint.
HATEOAS is critical to REST -- it being nothing more than a terribly obtuse rendition of the ideas behind how the web (but more importantly, the semantic web) works. This isn't the usual rant complaining about how HATEOAS is most implementers' afterthought; this is the rant about how HATEOAS is the entire damn point; without it you're just squirting JSON on the wire and using HTTP as a transport because it doesn't get blocked on a middlebox.
And now that we've retrofitted schemas into JSON, and moved half our APIs to use CORS/CSP-needing PUT/DELETE methods, and require OAuth for more than half of the requests, the original advantages of this scheme are entirely gone -- you can no longer just muck around in some half-baked javascript, parse out a single field, discard the rest and surface it in a 'web 2.0 mashup'. And when the vendor supplies the SDK anyway (even in Javascript), the exact form the messages take on the wire is entirely irrelevant. I hope the new wave of RPC (helped by the bi-directional multiplexing transport protocol inexplicably known as HTTP/2) becomes the new fad and kills this awful fumbling with cargo-cult fake-REST once and for all.
REST APIs must be hypertext-driven. http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hyperte...
https://jacobian.org/writing/rest-worst-practices/
Every web developer should read that ^
- API Anti-Patterns: How to Avoid Common REST Mistakes [1]
[0] https://www.infoq.com/articles/rest-anti-patterns
[1] http://www.programmableweb.com/news/api-anti-patterns-how-to...
I'm not really sold on this one since "close" is not a resource. I think I would do:
with data `{ "status": "closed" }`However, I know that the request body (a JSON string or a JSON array that could contain one or more queries, along with one or more different decryption keys allowing the server-side to retrieve the connection details that are encrypted on the server) could be quite long (we have some massive SQL queries that get used elsewhere and could potentially be used with this new API once it's available) and I also didn't like the idea of some of the more sensitive information being in the URL, so I decided to implement it as a POST request instead of a GET.
It feels wrong (again from the RESTful perspective since I'd like my API to be fully RESTful) but from a user point of view I know I'm making the right decision.
If anybody has better ideas though, please let me know!
Just because? No.
> The HTTP methods must be used to give the intent of the action that is happening.
That interpretation (PATCH, DELETE, etc) is not "correct", which is endlessly maddening. It was a possible scheme, just like a waterfall process was a possible development scheme. There is no correctness implied. See: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch..., http://cafe.elharo.com/web/why-rest-failed/, http://www.artima.com/lejava/articles/why_put_and_delete.htm..., http://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post
I really get sick of these purity guidelines that have no practical use other than to say "see, it works" which isn't compelling.
Question 2#: What is the consensus around custom verbs? While I've never gone down this route, a few times I have been tempted.
Also, PUT can conflict (or overwrite changes) if something else has altered the resource since you fetched it. PATCH is more granular, so you can make it a lot less likely to conflict or overwrite (in addition to being more efficient).
Re #2, I don't know about consensus, but usually when I think that I could use a custom verb, some more thought reveals that I could instead use a standard verb on another resource that I haven't spec'd out yet (like the comments about having a 'transaction' resource representing details of a transaction instead of a 'transfer' verb).