7 ms·
Except that RPC-structured APIs are complete garbage to consume for external developers because, surprise, they don't know or care what your internal function n
by mbleigh 12y ago
Except that RPC-structured APIs are complete garbage to consume for external developers because, surprise, they don't know or care what your internal function names are. And if you aren't thinking about the experience of real or theoretical third-party developers when building your API, why even have one at all?
If you ignore HTTP verbs and status codes you're just making it harder for other developers to grok your system. It's less about adherence to some strict standard and more about helpful signaling. A GET fetches data, POST creates new records, PUT is idempotent, DELETE deletes. 4xx status codes indicate a request error, 5xx indicate a server error. That's all useful information for someone trying to integrate with your API, but you think that instead everyone should memorize your in-house conventions. Good luck getting buy-in on that.
The only partial agreement I'd give to this is that I no longer think it's wise to nest multiple levels of resource IDs (e.g. /customer/123/order/456). This is, again, out of respect for external developers. Instead, I would have /customers/123 and /orders/456 (assuming that IDs can't be duplicated between customers). A flatter URL structure (which is, note, still REST-friendly) can be an easier-to-consume experience for other devs.
- Udik 12y agoWhy, are external developers able to consume restful APIs without the need for any documentation on the system they're talking to? Aren't the various REST entities (and all the possible links between them, if you're doing HATEOAS) already part of a protocol the developers of external consumers need to know beforehand?
- dragonwriter 12y ago> Why, are external developers able to consume restful APIs without the need for any documentation on the system they're talking to? In a fully RESTful, an external developer would be able to consume it with only documentation of the resource types used, and identification of a single base URI for a known resource from which the rest of the API's locations were discoverable. (That doesn't mean that an API owner wouldn't still want to document more, but it wouldn't be strictly necessary.)
- mbleigh 12y agoI'm speaking from the experience of a developer who has built numerous public APIs and client libraries over the years. Yes, you still need documentation for a REST API (I'm not a HATEOAS utopian), but you can easily attach your documentation to concepts with which many (probably most) developers are already familiar. The article is basically saying "You know how lots of cars have steering wheels and pedals? Well, I think it's annoying to build cars that way, so I'm going to use a system of levers and buttons." There's a ton of utility in anchoring new systems to familiar concepts, and unless you have a good reason not to you should strive to do so.
- drinchev 12y agoI totally agree. That's my first argument against the idea of the OP. In these days you have out-of-the-box so many already working API clients and already so many public API End-points that it would be really, really hard to create some ( opinionated ) better way of doing this. Libraries like Backbone and platforms like NodeJS ( and many more ) have this idea so deep that it becomes extremely easy to create an API front-end / back-end app with just a matter of a couple of files ( even lines ). This is becoming standard in other platforms as well. I think the debate is long time shifted from "Is REST good for my app?" to "How to best follow RESTful principles in my app?"
- a_c_s 12y agoSure, that sounds nice in theory. In the wild, how many developers (especially those working on internal APIs) have the time & inclination to thoroughly document their entire APIs? And if it exists, many developers read the entire spec of an API before beginning development? In my experience both are rare. Given the constraints developers often find themselves under, having an API that uses common patterns most developers already understand has great value. (Ex. 'GET /customer/123' performs a safe retrieval of a representation of the customer with ID 123, no documentation required) This leaves both developers (API creator & API consumer) free to focus on documenting/understanding the non-trivial parts of the API rather than reinventing the wheel.
- dragonwriter 12y agoNote that this use of conventional URI structure patterns does have the value you describe, but is also completely orthogonal to REST (REST isn't about beautiful URIs -- the path part of the URI could all be UUIDs and you could follow the REST architectural style perfectly; HATEOAS, in particular, means that other than the entry point, your URIs should all be coming to you from other documents with their context in that document telling you what they are, so you don't need to parse the URI for meaning.)
- a_c_s 12y agoYes, there is a difference betwixt 'true REST' (as defined by Roy Fielding in his PhD dissertation) and what most people refer to as REST. I was referring to the latter: in my experience 'true REST' APIs are rare in the wild.
- doktrin 12y agoHe didn't say external developers wouldn't need any documentation to consume a REST API. The implication made was that developers are more accustomed to REST, hence REST is easier to consume. Speaking for myself, I've found this to be true.
- jack9 12y ago> If you ignore HTTP verbs and status codes you're just making it harder for other developers to grok your system That's not what the article suggests (ignoring?). I totally disagree with maintaining (http verb) semantics for operations that can (and usually do) change over time. Simple JSON posts for everything make APIs simpler. There's nothing about convention, just APIs which may have irrelevant conventions.
- lmm 12y ago> Except that RPC-structured APIs are complete garbage to consume for external developers because, surprise, they don't know or care what your internal function names are. And if you aren't thinking about the experience of real or theoretical third-party developers when building your API, why even have one at all? They don't care what the internal function names are but they do care about the language of the domain, and if the internal program is well written then those will be one and the same. The HTTP verb model is inadequate for domains of realistic complexity; imagine a developer looking for the method that flubbleizes the worzwort. Is this POST /worzworts/12345 ? PATCH /worzworts/12345 ? If you squint at it then flubbleizing is kind of like adding another bishwiggle, so maybe it's PUT /worzworts/12345/bishwiggles ? Most people tend to realize that these domain-specific operations don't have a HTTP representation and instead make them a POST with the specific operation in the URL. But then you end up with an api that looks like: #flubbleize a worzwort POST /worzworts/12345/flubbleize #mrefgle a worzwort POST /worzworts/12345/mrefgle #chezzle a worzwort POST /worzworts/12345/chezzle #delete a worzwort DELETE /worzworts/12345 This ends up being less consistent, and more confusing to a developer, than simply using POST for every operation. > If you ignore HTTP verbs and status codes you're just making it harder for other developers to grok your system. It's less about adherence to some strict standard and more about helpful signaling. A GET fetches data, POST creates new records, PUT is idempotent, DELETE deletes. 4xx status codes indicate a request error, 5xx indicate a server error. That's all useful information for someone trying to integrate with your API, but you think that instead everyone should memorize your in-house conventions. Good luck getting buy-in on that. The developer doesn't start by knowing that something is a PUT and trying to figure out what it's doing - they start by knowing what they want to do and trying to figure out the call. The world isn't consistent enough for them to be able to guess, and splitting the call into two parts - verb and path - makes it harder to remember once you've looked it up, not easier. 4xx vs 5xx is maybe valuable, but the very fact that you xx it suggests that the difference between 412 and 422 probably isn't important. In practice every REST API I've seen has felt the need to include a response body that a) explains the error and b) includes their own API-specific error code. In which case, why repeat yourself badly in the HTTP status code?
- warfangle 12y agoMaybe it would make sense if you actually looked at it like a domain. I'm not sure of a domain where an operation cannot be represented as one of: * Return $data * Return a manipulation of $data that is the same type as $data * Return a value derived from $data * Update $data with a new value * Create a new identifier with values that conform to the same type as $data * Delete $data from the system Certainly there are things like transcoding a streaming video which don't seem to map immediately, but they could if you shoehorned them. You're probably better off using something like UDP for that, though, instead of an HTTP request over TCP/IP. Let's take images. Translation factor: worzworts -> image (identity) flubbleize -> encode to jpeg (non destructive manipulation) mrefgle -> encode to png (non destructive manipulation) chezzle -> execute image as a Piet program and return the output (derived value that is not necessarily of the same type) Now, it's think through it a little. POST /image/ <http://imgur.com/8XA9Eva> => 1 GET /image/1 -> identical response (except some http headers) to the imgur URL GET /image/1/piet -> "Piet" GET /image/1/jpeg -> /image/1 re-encoded as a jpeg All good here. But let's look at some of the operations you suggest might be used, and the action they take: POST /image/1/jpeg -> re-encodes the gif as a jpeg and saves it to /image/1; does not conform to REST best practices because POST is for creating new records -- immediately the developer is confused GET /image/1/piet -> <unknown, I do not have a Piet runtime> because jpeg encoding is lossy, it fundamentally changes the data source at /image/1 with an arbitrary filter; in this case, JPEG compression. Funky undefined behavior, and it may not have the side effects intended. Now let's see how it would work RESTfully: GET /image/1/jpeg -> returns a jpeg encoding of /image/1 POST /image/ <response from /image/1/jpeg> -> 200 OK, { id: 2 } POST /image/ "Piet" -> Error 415 (images expect an image, not a string) GET /image/2 -> returns jpeg encoding of /image/1 GET /image/1/piet -> "Piet" PUT /image/2 <response from /image/1/png> -> 200 OK GET /image/2/piet -> "Piet" (since png is a lossless encoding, Piet will perform the same on it) If you conform your API to single responsibilities, you won't confuse the consumers of it by transforming a data source in-place on their request. Things go a little sideways, but not much, if these operations need parameters. But that's why they're query parameters and not a part of the url. I don't think anyone would suggest that you create a URL like /image/1/jpeg/width/640/height/480 That's just silly. If you follow the single-resource-deep philosophy, though, anything after the ID is a resource that is derived from the resource residing at #ID. This URL might actually make a lot of sense: /image/1/jpeg/resize/640x480 But then again, so would: /image/1/resize/640x480/jpeg But wait, there's an even better way to define these ... and still be restful. /image/1.jpeg, /image/1.png, /image/1.piet Oops, that last one doesn't work. Piet isn't an image format, it's the result of executing the image as a Piet program. So maybe that one works better as a URL segment: /image/1.jpeg (image), /image/1.png (image), /image/1/piet (string) In this case, /image/1.jpeg?w=640&h=480 makes just as much sense as /image/1.png?w=640&h=480 But since we know that instead of a transformed image, the image/:id/piet resource is a derived string based on image/:id, this URL totally doesn't make sense - and neither should it! Should the image be resized before being executed? How exactly does one change the height of an ASCII string? Maybe you want an image of the ASCII string that has been resized? Ambiguous request! Undefined behavior! /image/1/piet?w=640&480 -> 400, bad request. But these same operations could be done on a video stream. POST creates a new video stream identifier; streaming source opens a websocket/webrtc/whatever based on that identifier; GET /stream/1/hls -> hls chunked stream of the incoming source video; GET /stream/1/dash -> DASH chunked stream of the incoming source video. Uh oh, source stream disconnects due to a network glitch. That's okay, they can just re-open it with the same identifier. Source stream PUT <some kind of manifest> /stream/1 : notifies the server that it is complete, server can cease to accept new streaming inputs for /stream/1; GET /stream/1/hls now returns a 301 redirect to /video/1/hls, which contains the entirety of the video that was streamed, as the server received it. Or do you have some other domain in mind that doesn't involve flubber?