[HN Gopher] How to (and how not to) design REST APIs
       ___________________________________________________________________
        
       How to (and how not to) design REST APIs
        
       Author : stickfigure
       Score  : 95 points
       Date   : 2023-11-01 19:06 UTC (3 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | saikatsg wrote:
       | Liked the idea of using wiki as blog
        
         | bitwrangler wrote:
         | he actually writes about that. I happen to like it too.
         | 
         | https://github.com/stickfigure/blog/wiki/GitHub%27s-wiki-mak...
        
       | nesarkvechnep wrote:
       | Half of the points are complete bs. URLs don't matter in REST.
       | What matters are link relations.
        
         | 541 wrote:
         | Care to elaborate briefly on why they do?
        
           | lmz wrote:
           | "REST" is supposed to be an architectural style reflecting
           | the Web as used by humans. Humans don't normally manually
           | construct URLs. They navigate based on links from an entry
           | point. (See also: https://hypermedia.systems/hypermedia-
           | components/#_self_desc... )
        
             | tkiolp4 wrote:
             | The vast majority of people using REST do not follow its
             | original definition, so the original definition doesn't
             | matter anymore.
             | 
             | It's like human languages: REST is whatever we make of it,
             | regardless of what academics say.
        
         | thedougd wrote:
         | HATEOAS adds that to REST.
        
         | DrDroop wrote:
         | I would go even further and say that REST is BS Most people
         | understand REST as a format for URLs and a particular semantics
         | for HTTP methods. While it can be perfectly ok to design an api
         | like person?id=123. This is the way that Postgrest does it,
         | which works, and makes certain things like entities with
         | composite primary keys way easier.
         | 
         | Other, like OP, think REST isn't going far enough in that
         | HATEOAS is what "really" makes something restfull. This doesn't
         | really work in practice as if changing the schema of some JSON
         | response can magically change the behaviour of an application.
         | I you want to lift all the logic to the server, I guess you can
         | use htmx, but then you are not building an api anymore but a
         | remote rendering engine.
        
       | switch007 wrote:
       | Some good points - particularly about not returning arrays (I've
       | made that mistake!)
       | 
       | But I feel 410 instead of 404 is pretty controversial:
       | 
       | > There are many layers of software that can return 404 to a
       | request
       | 
       | Anything in your stack can return any HTTP error code - I don't
       | see why 404 is special.
       | 
       | > When calling (say) GET /things/{thing_id} for a thing that
       | doesn't exist, the response should indicate that 1) the server
       | understood your request, and 2) the thing wasn't found.
       | Unfortunately, a 404 response does not guarantee #1.
       | 
       | The server is free to return other codes for other classes of
       | problems. The server could return 400 for a bad request, and
       | leave 404 for "thing wasn't found", indicating it understood the
       | request but it wasn't found.
       | 
       | Also surprised not to see RFC 7807 / RFC 9457 (Problem Details)
       | not mentioned in the "structured error format" section.
        
         | theandrewbailey wrote:
         | That rule is a hot take.
         | 
         | > You could use 404 but return a custom error body and demand
         | that clients check for a correct error body. This is asking for
         | trouble from lazy client programmers. It might or might not be
         | "your fault" when clients see eventually inconsistent data, but
         | the support calls they send you will be real.
         | 
         | Make sure that your 404 responses were always documented, then
         | tell them to RTFM.
        
           | stickfigure wrote:
           | The problem is that the support call comes in as "your DELETE
           | call isn't actually deleting". Sure it's not your fault, but
           | it imposes a cost on you to investigate. And of course the
           | first time you go directly to RTFM without checking will be
           | the time it actually is your bug.
           | 
           | 404 is special because it's so incredibly common. Why take
           | the risk? There are other perfectly good error codes that -
           | in practice - don't have this issue.
        
         | DSMan195276 wrote:
         | > Anything in your stack can return any HTTP error code - I
         | don't see why 404 is special.
         | 
         | I'm surprised you don't - in my experience 404's are by far the
         | most common response to get when you haven't wired things up
         | correctly. Sure anything in the stack _can_ return any code and
         | response they want, but you're still much more unlikely to come
         | across a 410 rather than 404. If that unlikeliness saves you
         | support calls down the line then that's pretty good.
        
           | layer8 wrote:
           | With REST 404s due to missing resources (non-existing ID),
           | you should generally get corresponding error information in
           | the body (as also described in TFA), and clients should
           | log/display that information. That should make enough of a
           | difference. There's a lot of "should" here, of course, but
           | instead of teaching developers to not use 404, it would be
           | better to teach them to create and handle error responses
           | appropriately.
        
         | noiv wrote:
         | I prefer to consider 404 as protocol error and missing thing as
         | business error. That way 404 signals wrong endpoint and 200 +
         | error message + empty result set signals wrong id.
         | 
         | Or even more simple: Anything other than 200 means check
         | infrastructure docs and if you don't like the 200 check the
         | business requirements.
        
           | stickfigure wrote:
           | As a client I generally dislike APIs that use 200 for error
           | conditions. The problem is that API implementors often change
           | the structure of the response.                   GET
           | /thing/THG123         # on success:         {"id":"THG123",
           | "name":"thingie"}         # on failure:         {"error":"no
           | such thing"}
           | 
           | Working in typed languages, this requires parsing the
           | response, determining success or failure, then reparsing the
           | response into the appropriate type. Annoying.
           | 
           | Of course it's not always like that, some APIs will put both
           | the error and data in a wrapper object and one field or the
           | other will always be null:                   {
           | "error": null,             "result": {"id":"THG123",
           | "name":"thingie"}         }
           | 
           | This is less annoying but it's still tedious. We could
           | eliminate the wrapper if we only had an out-of-band signal to
           | indicate whether the client should expect a success response
           | or an error response... like maybe an HTTP status code? I
           | mean, it's right there, why not use it?
        
         | esafak wrote:
         | > Some good points - particularly about not returning arrays.
         | 
         | I don't get that one; why is an object with an array property
         | more evolution friendly than an array of objects?
        
           | layer8 wrote:
           | Because you can add new properties for response-level global
           | information on an object, but not on an array.
        
             | esafak wrote:
             | Obviously if it was global, but if not, you'd include it in
             | the objects in the array, no? This is not a question of
             | schema evolution per se.
        
       | AlexandrB wrote:
       | Found a contradiction that I don't understand. From rule 1:
       | # GOOD         GET /products   # get all the products         GET
       | /products/{product_id} # get one product                  # BAD
       | GET /product/{product_id}
       | 
       | But then in Rule 2:                   GET
       | /shop/{shop_id}/listings              # normal, expected
       | 
       | Shouldn't that be "/shops/{shop_id}/listings"? Or is it plural
       | only if you can actually GET the path (i.e. there's no GET for
       | just "/shop") and otherwise it should be singular?
        
         | stickfigure wrote:
         | This is the Etsy API, but actually was a typo on my part. They
         | use the plural /shops (as I showed in the other Etsy example).
         | I've corrected the original article, sorry about that!
        
         | robertlagrant wrote:
         | Plural or singular seems far too marginal to be good or bad. I
         | use singular.
        
           | iterati wrote:
           | Same. My table names are also singular.
        
           | prisonality wrote:
           | I use singular too, but I always wonder to those sticking
           | with plural - what's the convention for words which plural
           | and singular are the same.
           | 
           | ie - like 'staff' or 'species' or 'aircraft'.
           | 
           | Then I can add suffix to those singular ie - 'staffList',
           | 'speciesList' etc
        
       | teaearlgraycold wrote:
       | Regarding #8 - just do not use http status codes for application
       | errors. They are for routers, caches and proxies. Your
       | application should pretty much only return 200 even on errors.
       | 
       | Edit: Bring on the downvotes. I will die on this hill.
        
         | hooverd wrote:
         | I see SOAP has entered the chat.
        
           | pskinner wrote:
           | *graphql
        
         | throw_m239339 wrote:
         | > Regarding #8 - just do not use http status codes for
         | application errors. They are for routers, caches and proxies.
         | Your application should pretty much only return 200 even on
         | errors.
         | 
         | What is an application error? If a user tries to query a
         | ressource they are not authorized access to, then returning a
         | 401 is appropriate, if the resource doesn't exist then 404 is
         | also appropriate, in theory (maybe not in practice for security
         | reasons but whatever). Nothing wrong with that.
         | 
         | HTTP codes are not made only for routers, caches and proxies,
         | HTTP was made for user agents such as browsers, HTTP is one of
         | the foundations of REST.
         | 
         | and I didn't downvote you, I'm just asking a question.
        
           | teaearlgraycold wrote:
           | To be fair I was being a tiny bit inflammatory. I think it's
           | appropriate to use HTTP codes sparingly. Personally I'll use
           | 401 and 500 but everything else is 200. And if the API is
           | meant for public consumption by 3rd party devs then meeting
           | expectations is more important than my personal philosophy.
           | 
           | But basically the overlap between the classic HTTP status
           | codes and your API's functionality is IMO coincidence. Unless
           | you're building a BLOB store or HTTP middleware you probably
           | do not have enough overlap for it to be truly appropriate for
           | your domain. HTTP is the envelope. It doesn't need to mix
           | with your custom JSON API.
        
         | pie_flavor wrote:
         | I don't know why you're being downvoted. I think you're
         | completely wrong, but it's not like you're being rude about it.
         | 
         | Status codes are not necessarily useful for the developer
         | _directly_ , as they're a second channel for the same
         | information, but they are useful for middleware of all kinds
         | (your argument applies equally to browsers showing errors to
         | users, and the same counterarguments apply to programmers). For
         | example, the HTTP library I'm using has an error_for_status
         | function which conveniently raises a runtime error, without me
         | having to dig into the response to do that manually. Also, if
         | you invent more kinds of errors later, or even if you just
         | haven't published a master table of errors where I can see it,
         | the status code will still let me extract useful semantic
         | information out of an error kind my code has not been written
         | explicitly to handle.
        
       | aero142 wrote:
       | Doing REST microservices is incredibly slow because of the amount
       | of work it is to agree on what a "clean" and "consistent" api
       | looks like for each service. It's just such an endless well of
       | trying to establish best practices without refactoring
       | constantly.
       | 
       | It's worth it for your public API, but it's such a huge time sink
       | for internal APIs.
        
         | wargames wrote:
         | I would be interested in what you are using internally that is
         | allowing you to move quicker.
        
       | elevation wrote:
       | Rule #1 is terrible advice.
       | 
       | Avoid plural nouns in English API endpoints because English is
       | full of irregular plurals. For example:
       | 
       | goose -> geese child -> children index -> indices vertex ->
       | vertexes analysis -> analyses
       | 
       | This makes English plurals unpredictable especially for for non-
       | native speakers and hurts API consistency and discoverability.
       | 
       | Also consider that for a CRUD interface you may need the singular
       | form anyway (POST api/student/create), and adding the plural
       | means doubling the API route namespace.
       | 
       | It's cleaner and simpler to stick with singular nouns.
        
         | marcellus23 wrote:
         | > for a CRUD interface you may need the singular form anyway
         | (POST api/student/create)
         | 
         | Why? What's wrong with api/students/create?
         | 
         | > Avoid plural nouns in English API endpoints because English
         | is full of irregular plurals.
         | 
         | I don't buy this. I mean, yes, it's true, but how often do
         | people really need to write these endpoints after initially
         | writing the client code?
        
         | stickfigure wrote:
         | You use plurals anyway to fetch collections:
         | GET /students
         | 
         | So you can't escape the problem unless you want `GET /child` to
         | fetch multiple children.
         | 
         | Also, you should avoid verbs in URLs (IMHO, of course). You're
         | adding to the students collection, so post to students:
         | # BAD         POST /student/create              # GOOD
         | POST /students
        
           | pavlov wrote:
           | Does "GET /students" return all the students in the system?
           | Probably not.
           | 
           | So in fact you're fetching some subset of students anyway,
           | and the size of the returned set might be one or zero
           | depending on your query.
           | 
           | Given that, "GET /student" seems just as meaningful because
           | neither the singular nor the plural can fix the ambiguity
           | about what you're actually getting.
        
             | stickfigure wrote:
             | Yes, I would expect GET /students to return all of the
             | students (or at least, all of the students visible to me).
             | Typically with query parameters for filtering:
             | GET /students?min_age=20
             | 
             | Alternatively, `students` might be a collection attribute
             | on another resource:                   GET
             | /classes/{class_id}/students
        
             | zdragnar wrote:
             | I expect singular to return one, and plural to return a
             | set, which could range from 0...n.
             | 
             | Likewise, I wouldn't expect a singular to return a single
             | object wrapped in an array, but I would always expect
             | "/plural" (with no further qualifier in the url) to return
             | an array, regardless of 0, 1 or more results.
             | 
             | Why would returning a full set be a condition of whether or
             | not plural is ambiguous?
        
           | bonzini wrote:
           | "POST /students" is a create action, but verbs are fine for
           | individual entities, for example "POST /students/ID/enroll".
        
           | 3cats-in-a-coat wrote:
           | An API is not an essay, in OOP you write Array<Student> and
           | not Array<Students> and yet you understand the type is about
           | an array of students. Getting hung up on grammar in an API is
           | probably the dumbest problem to have.
           | 
           | If you think `GET /student` is confusing, or more
           | importantly, structurally restrictive as an API, you can
           | think about it as `GET /student/filter` where the "filter"
           | may be a specific student id, or a range of ids, or other
           | conditions such as `GET /student/top` or `GET
           | /student/graduated` and then all students will be just the
           | filter "all" or: `GET /student/all`.
           | 
           | As for `POST /student/create`... it doesn't matter. To use
           | one of Fielding's own examples from his blog, how'd you turn
           | a lamp on and off via REST? Would you be like `POST /lamp`?
           | No. It's unclear WTF is happening.
        
             | hnbad wrote:
             | > Fielding's own examples from his blog, how'd you turn a
             | lamp on and off via REST? Would you be like `POST /lamp`?
             | No. It's unclear WTF is happening.
             | 
             | No, of course you'd be like `PATCH {"light": "off"} /lamp`!
             | 
             | Kidding of course but it's true that REST purity does not
             | make for intuitive APIs in complex real-world problem
             | domains.
        
           | jordanrobinson wrote:
           | While I do agree with this in almost all cases, I have found
           | scenarios where there are actions that don't map easily to a
           | HTTP verb and need something more explicit.
           | 
           | What I've generally done in these cases is pretty similar to
           | https://cloud.google.com/apis/design/custom_methods which
           | also explains the problem better than I can.
           | 
           | I'd be interested as to how you'd solve some of these
           | problems without an explicit verb in the path.
        
             | stickfigure wrote:
             | I'm not a purist; for unusual edge cases, I'll put a verb
             | (or something appropriate to the context) at the end of the
             | path. But `create` isn't unusual, just POST to a
             | collection.
        
           | prisonality wrote:
           | how do you differentiate between plural vs singular of:
           | 
           | `GET /staff`
           | 
           | ?
        
             | stickfigure wrote:
             | I don't? It's fine. I'm also fine just adding an 's' to
             | many words that have unusual plurals; English is flexible,
             | and "persons" is a perfectly acceptable substitute for
             | "people".
             | 
             | That said, I don't love your example. Staff does have a
             | plural, staffs - as in, the separate staffs of multiple
             | organizations.
        
               | prisonality wrote:
               | what's your opinion of using suffix like '_list' to
               | differentiate it ?
               | 
               | ie:
               | 
               | GET /species
               | 
               | and
               | 
               | GET /species_list
               | 
               | ?
        
         | takinola wrote:
         | You really should not include the action in the URL ie rather
         | than
         | 
         | GET api/student
         | 
         | POST api/student/create
         | 
         | DELETE api/student
         | 
         | it should be
         | 
         | POST api/students
         | 
         | GET api/students
         | 
         | DELETE api/students
        
         | golergka wrote:
         | It gets even more unpredictable for everyone involved when it's
         | a non-native speaker who writes the API schema.
        
       | sigmonsays wrote:
       | are we beating a dead horse here?
       | 
       | Havn't we talked about building REST APIs enough yet?
        
         | n42 wrote:
         | unequivocally
        
         | RedShift1 wrote:
         | Hello there. Have you heard of our lord and savior, GraphQL?
        
         | HatchedLake721 wrote:
         | There's a new generation that didn't bikeshed about singular or
         | plural nouns in the API endpoints. Let them be?
        
       | physicsguy wrote:
       | I'd add:
       | 
       | * If you're going to forbid people changing a parameter with a
       | PUT or PATCH request, then the schema for these shouldn't list
       | them as parameters. This seems to creep in to APIs constantly as
       | people are lazy and will use the same serializer method as for
       | POST with an additional check somewhere in the code that changes
       | the response. Just don't do it!
       | 
       | * Don't change the response format based on query parameters. It
       | makes it hard for typed languages to use the API because the
       | client has to handle all of the weird response types you've got.
       | Inevitably you end up with more and more getting added and it any
       | client becomes crazily complicated. 99% of the time it's not
       | worth the bandwidth saving - and if there's lots of useless
       | information that clients don't want, it's worth thinking about
       | whether the API design is right in the first place.
       | 
       | * Stick to one mechanism for doing things. Pagination and sorting
       | behaviour should be the same for all endpoints. The end user
       | doesn't care that you're a hip microservices company where teams
       | don't talk to each other - if the APIs behave weirdly and
       | inconsistently between themselves, it will be hard to use.
        
         | janfoeh wrote:
         | I very much agree with your first and third point, from
         | experience. As for the second one -- if consuming dynamic data
         | structures is hard in typed languages, maybe they are not the
         | right tool for that particular job?
         | 
         | What I have seen is endpoints trying to corral their responses
         | into one-size-fits-all schemas in the situation you're
         | describing, with predictable outcomes. Lots of overhead in most
         | situations, tricky documentation, lots of optionals.
         | 
         | Under that premise, I have to say that at least for generic
         | APIs with many differing clients, the idiosyncrasies of typed-
         | language clients would not rank too highly on my list of design
         | considerations -- not when they are in the way of simpler,
         | easier to understand responses.
        
           | Joker_vD wrote:
           | As for the second point, that's what Accept header is for.
           | And I personally never had much trouble in Go with
           | deserializing all those "weird response types" but it may
           | depend on one's coding style.
           | 
           | > I have to say that at least for generic APIs with many
           | differing clients, the idiosyncrasies of typed-language
           | clients would not rank too highly on my list of design
           | considerations
           | 
           | Hey, would you like to consume an exchange format that has
           | meaningful distinction between strings and atoms? Those come
           | from the dynamically-typed languages area!
        
       | corytheboyd wrote:
       | It all turns into RPC anyway, I just can't care that much about
       | REST anymore
        
       | diarrhea wrote:
       | Experienced point 6 with the GitHub API. "Repository" is a core
       | Git(Hub) primitive, and across the entire API surface (they have
       | an OpenAPI spec, 40 MB in size total), last I counted it was 48
       | different versions of repository. For example, what's considered
       | a repo owned by a user might be different from that of an
       | organisation. There is no sane recourse but automatic code
       | generation, which is a ton of effort in itself (tooling isn't
       | great).
        
       | gensym wrote:
       | > REST APIs
       | 
       | "You keep using that word. I do not think it means what you think
       | it means".
       | 
       | https://ics.uci.edu/~fielding/pubs/dissertation/top.htm
        
       | n42 wrote:
       | I feel like at this point I have heard convincing arguments for
       | and against basically every point in this article, every article
       | like it, and every comment on them.
       | 
       | Hot take: it doesn't matter. If the user cared enough about your
       | decisions to file a bug report or a complaint, you're doing
       | something right. Consider that a success.
       | 
       | Stop trying to shoehorn a creative outlet into your day job. Pick
       | someone else's terrible design and stick to it.
       | 
       | Frankly I am shocked to see an article on REST design on HN in
       | 2023. We sort of figured this one out.
        
       | fsaintjacques wrote:
       | I highly recommend anyone to read Google's
       | [AIP](https://google.aip.dev/). There's even a grpc schema linter
       | for it. Put more focus on the resource data design than
       | nitpicking on transport details. I would consider the best
       | lessons to be:
       | 
       | - Optional but supported user defined identifiers, it's so
       | frustrating to work with API that passes you back an identifier.
       | 
       | - String identifier (names) for resources, with some kind of type
       | namespacing, i.e. the prefix in the author's document -
       | Consistent set of fields (create_time, update_time, annotations,
       | ...)
       | 
       | - Avoid dynamic map (this is a JSON self-inflicted wound)
        
         | aleksiy123 wrote:
         | Second this. Reading this while working at google made me
         | better design. Some that stand out to me are.
         | 
         | Resource Oriented Design: https://google.aip.dev/121
         | 
         | Declarative Friendly APIs: https://google.aip.dev/128
         | 
         | Declarative friendly makes writing scripts, pipelines so much
         | better because of idempotency. It also pairs very naturally
         | with resource Oriented design.
         | 
         | Long Running Operations: https://google.aip.dev/151
         | 
         | LROs are applicable to any request that runs longer than a
         | second or a couple of seconds. Having a unified interface can
         | be very powerful for implementing offline task workers and
         | pipelines.
         | 
         | Filtering: https://google.aip.dev/160
         | 
         | This one is probably controversial as it's makes implementing
         | basic filtering quite a bit harder. I haven't quite seen the
         | issues it's supposed to solve play out in practice but it's
         | interesting nonetheless.
        
       | 3cats-in-a-coat wrote:
       | We need to forget this word REST, because all advice about it is
       | superlame, regardless of the original paper.
        
       | alxmng wrote:
       | "RESTful" API design is mostly bike-shedding.
       | 
       | There's no standard. Every REST API looks different. Clients have
       | to refer to documentation anyway, so consistent URL patterns
       | achieve nothing. People waste large amounts of time over totally
       | inconsequential minutiae like whether to use singular or plural
       | words in URLs.
       | 
       | Separating idempotent calls from non-idempotent calls is useful,
       | but REST overcomplicates this. All that's needed is read and
       | write calls, yet REST has get, post, patch, put, delete...
       | 
       | REST is also inefficient. Clients could read the data they need
       | in one HTTP request, but most "RESTful" APIs force clients to
       | make many requests for the sake of what is essentially
       | aesthetics.
        
         | tkiolp4 wrote:
         | Agree. But I pick REST (or "json over http") any day of the
         | week instead of graphql, soap, grpc, etc.
        
           | nine_zeros wrote:
           | Graphql just for the sake of graphql is a disaster for
           | backend engineers.
        
         | waynesonfire wrote:
         | What's your view on this,
         | 
         | https://news.ycombinator.com/item?id=38103310#38104983
         | 
         | ?
        
       | charlus wrote:
       | Rule #11, and the general text there on idempotence was very
       | interesting and I definitely learnt nicer ways of handling this,
       | thank you.
        
       | postalrat wrote:
       | A missing rule is "DON'T use strings for timestamps". Which
       | implies "Rule #6: DO use strings for all identifiers" is not good
       | advice.
        
       | jmull wrote:
       | There are a bunch of bad rules here...
       | 
       | Rule 1 - "DO use plural nouns for collections" - is an entirely
       | arbitrary opinion.
       | 
       | Rule 2 - "DON'T add unnecessary path segments" - I agree with the
       | rule, but the examples are bad because, e.g.,
       | "/listings/{listing_id} " and
       | "/shop/{shop_id}/listings/{listing_id}" mean two different things
       | (or at least they should). Now,
       | "/shop/{shop_id}/listings/{listing_id}" is a complex path, so if
       | your API doesn't need it, then I agree, don't include it. But if
       | it does, then it would be bad to not include it.
       | 
       | Rule 3 - "DON'T add .json or other extensions to the url" I
       | mostly agree with the rule, but on the grounds of keeping things
       | simple. Here, keeping to the standard (which means using Accept).
       | But things like supporting a ".json" suffix are nice for cases
       | where you want to give people (not programs) access to the
       | different representations (should be in addition to Accept).
       | 
       | This justification for rule 3: "URLs are resource identifiers..."
       | is simply not true, at least for any reasonably useful definition
       | of identifier. A URL points at a resource, that's it.
       | 
       | Rule 4 is good. ("Rule #4: DON'T return arrays as top level
       | responses") You want to keep the door open to adding metadata in
       | the response body that will be very easy for clients to accept in
       | a backwards-compatible manner.
       | 
       | Rule 5 "DON'T return map structures" doesn't really make sense.
       | Now, you shouldn't do it just to provide a lookup index -- the id
       | should really be the inherent id of the data -- but it's a
       | logically valid way to structure data and your API should strive
       | to match the logical structure of the data. Also, the arguments
       | here are not great.. e.g., "Converting an array of objects to a
       | map is a one-liner in most languages"... that's true, but so is
       | the converse. The openapi example doesn't make sense either.
       | openapi v4 _could_ have simply added a  "name" property to the
       | object in the v3 structure, right next to the "post" property --
       | just like the hypothetical list-based API. I would assume openapi
       | has other reasons for the restructure, because the map-based API
       | doesn't force it.
       | 
       | Well, I'll stop there. It's not all bad, but just don't take
       | these rules to the bank.
       | 
       | Wait one more: Rule 8 "DON'T use 404 to indicate not found"
       | 
       | Come on now, why even write something like that?
       | 
       | The rule is more like, don't use 404 poorly. DELETE should be
       | idempotent (that's a good rule), which means an attempt to DELETE
       | something that could exist but doesn't happen to exist right now,
       | isn't an error, and should return a 2xx code. 404 response for an
       | attempt to delete on a route that doesn't exists makes sense
       | (well, I guess unless your API is so dynamic that routes can be
       | created and deleted on the fly, in which case even there you'd
       | return a success code).
        
       | jameshart wrote:
       | This falls down as soon as it makes a fundamental
       | misunderstanding of what makes a REST api into a REST api.
       | 
       | It gives this as a 'bad' example:                  GET
       | /v3/application/shops/{shop_id}/listings/{listing_id}/properties
       | 
       | With the justification that "The {listing_id} is globally unique;
       | there's no reason for {shop_id} to be part of the URL. "
       | 
       | No the point of the API is that
       | /v3/application/shops/{shop_id}/listings/{listing_id}/properties
       | is a globally unique identifier. Your belief that parts of that
       | id have global meaning outside the context of that identifier is
       | irrelevant - that _path_ is the identifier for the resource.
       | 
       | And having hierarchical paths is useful because you can do things
       | like manage permissions on parts of the hierarchy - users might
       | have permission to check listings in certain shops and we can
       | characterize that as them having permission on
       | /v3/application/shops/{shop_id}/listings/*.
       | 
       | Directory structures of resource identifiers are good and logical
       | and not a 'bad' API design practice at all. You might as well
       | argue the UNIX file system is a bad design because all the files
       | have a unique inode id so paths are completely unnecessary.
        
         | stickfigure wrote:
         | You apparently have not actually used Etsy's API.
         | 
         | No, the shop id is _not_ in fact part of the globally unique
         | identifier of an Etsy listing, and the properties are not
         | dependent on the shop. Etsy listings have a 1:N relationship
         | with Etsy shops.
         | 
         | The API was a mistake, which they are slowly correcting -
         | they've already changed:                   GET
         | /v3/application/shops/{shop_id}/listings/{listing_id}
         | 
         | to:                   GET /v3/application/listings/{listing_id}
         | 
         | ...and I presume they will eventually change the rest of the
         | listing-related endpoints over time.
         | 
         | Managing permissions using the hierarchy of a URL is silly at
         | best, dangerous at worst. The first thing any attacker will do
         | is plug in an alternative shop id and see if it grants access
         | to the non-permitted listing. If permissions are attached to
         | the shop (and for Etsy, they are) the server needs to load the
         | listing, figure out the associated shop, and then check
         | permissions. The client cannot be trusted to provide the
         | correct shop id, so there's no point in asking for it.
        
       | sndsgd wrote:
       | Regarding #2, I'm guessing the listings are sharded by shop_id,
       | so without it you'll need to query all database shards.
       | 
       | Is there a better place to include the sharding key in a REST
       | request?
        
       | henning wrote:
       | Telling someone you should not return 404 for not found is a
       | great way to not get hired at most tech companies.
        
       | waynesonfire wrote:
       | Regarding #9 BE consistent: this is the consequence of allowing
       | chaos to take over. There are far fewer individuals trying to
       | reduce complexity compared to those who are adding to it. And,
       | the effort required to resolve these inconsistencies is 10x than
       | that needed to create them.
        
       | waynesonfire wrote:
       | The author makes a good point about not throwing 404s.. a logical
       | extension of this is that it should apply to all the other error
       | codes. Does REST encourage the use of http error codes?
        
       | layer8 wrote:
       | Regarding rule #4 (DON'T return arrays as top level responses), I
       | feel that meta information returned about the collection really
       | fits the responsibility of HTTP headers. This is similar to
       | existing response headers like Content-Length and Content-Range.
       | REST clients already work with "out-of-band" information like
       | HTTP statuses and e.g. If-Modified-Since. Do we always have add
       | yet another layer to nest meta information in?
        
         | hoherd wrote:
         | While you may be right about headers, I think the point the
         | article makes about easily adding more fields to the result
         | without breaking backwards compatibility is pretty compelling.
        
           | layer8 wrote:
           | It's a good rule for non-collection resources and for the
           | _elements_ of collection resources, but I was specifically
           | thinking about collections, where all top-level information
           | (apart from the elements of the collection themselves) is
           | necessarily meta information about the returned data, and not
           | part of the returned data proper.
        
         | waynesonfire wrote:
         | I struggle with this concept with RabbitMQ as well. The AMQP
         | 0-9-1 protocol used by RabbitMQ has a headers table at the
         | protocol level for user-defined key-value pairs that can be
         | associated with the message payload. The same question applies
         | here, what should go in this protocol header table vs on the
         | message.
         | 
         | One concern I have about using these user-defined headers is
         | that in my designs I'll typically remove the payload from the
         | AMQP envelope and propogate just the payload to the business
         | logic. If the headers need to be included along with the
         | business logic, there is the issue of propagating them. It
         | seems risky to use the headers at the protocol level.
         | 
         | Any thoughts?
        
       ___________________________________________________________________
       (page generated 2023-11-01 23:01 UTC)