How to (and how not to) design REST APIs
121–130 of 153 posts
Re: How to (and how not to) design REST APIs
#122I’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…
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 doc…
GET /api/object/?withAdditionalMetadata=1&expandChildren=1&.....
So then the OpenAPI schema has to be something like: schema:
oneOf:
- $ref: '#/components/schemas/Object'
- $ref: '#/components/schemas/ObjectWithMetadata'
- $ref: '#/components/schemas/ObjectWithChildren'
- $ref: '#/components/schemas/ObjectWithChildrenAndMetadata
So inevitably the client ends up being quite complex to handle this.Re: How to (and how not to) design REST APIs
#123Earlier quoted context omitted.
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/{…
You apparently haven't read Fielding's paper. Etsy isn't doing it right. If you read Fielding's paper, OP's point is correct. The whole URL is a resource.
Re: How to (and how not to) design REST APIs
#124Earlier quoted context omitted.
An API is not an essay, in OOP you write Array and not Array 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…
I think just about anyone who uses a name referencing an Array will give it the name "students". And it does often end up mattering, for clarity where at its use sites where you won't have the type declaration to help you out, and for having "student" available as a name in the same scope, as it's typical to pull an item out of a collection. /student/all looks particularly icky to me. For getting a student, it seems…
Meanwhile, APIs, as interfaces, are forever. And the dumbest thing to do is to decide to have two names for one type in an interface, because grammar happens to have single and plural version for words. Why would you do that? APIs have no grammar, they're not sentences, they're made of identifiers that need to uniquely identify something. So stop trying to force grammar in.
Re: How to (and how not to) design REST APIs
#125Earlier quoted context omitted.
An API is not an essay, in OOP you write Array and not Array 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…
If I use turning a lamp on and off, I'd do GET /lamp and in the response there should be an href with a rel of "on" that I can follow (in JSON land). In HTML, I might have a FORM that does a POST of the lamp's switch to /lamp/switch. Because the switch is the resource that you're trying to manipulate when turning a lamp on/off, not the lamp itself.
Re: How to (and how not to) design REST APIs
#126Earlier quoted context omitted.
> 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.
In HTML, you could have a FORM with the URL of /lamp/switch that you PUT.
Re: How to (and how not to) design REST APIs
#127This 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}…
The author gives a reason for that recommendation > [having shop_id in the URL] inevitably causes problems when your invariant changes down the road - say, a listing moves to a different store or can be listed in multiple stores. Basically the choice is between having a perpetual unique URL to a listing or multiple ones, maybe valid at the same time and some of them maybe invalid in future, when a listing is removed…
REST does not mean ‘parameters in the path not in the query string’.
Re: How to (and how not to) design REST APIs
#128This 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}…
You're sacrificing usefulness for purity. Never a good bet. I agree with the author.
If you are designing a ‘REST API’ you have already committed to ‘purity’. If you follow this guidance you are not designing a REST API you are designing a JSON over HTTP api with parameters in the query string.
Re: How to (and how not to) design REST APIs
#129Earlier quoted context omitted.
I'm on your side, but every time you say "a hypermedia" it kills me.
sorry, an hypermedia
(The effect isn't ameliorated by modifiers, e.g. "a natural hypermedia".)
Re: How to (and how not to) design REST APIs
#130Earlier quoted context omitted.
The author gives a reason for that recommendation > [having shop_id in the URL] inevitably causes problems when your invariant changes down the road - say, a listing moves to a different store or can be listed in multiple stores. Basically the choice is between having a perpetual unique URL to a listing or multiple ones, maybe valid at the same time and some of them maybe invalid in future, when a listing is removed…
That’s what 302 responses are for. REST does not mean ‘parameters in the path not in the query string’.
Suppose that we have
/shops/1/listing/1
/shops/2/listing/1
where listing 1 is an id local to those shops, they are two different listings. What happens to those URLs when those shops remove those listings? Both of them should return 404.Then the listing appears in shops 3 and 4.
/shops/3/listing/1
/shops/4/listing/1
If we have four different records in the listings table of the database there is usually no way to relate those listings, unless we inspect all the records after creates and updates looking for exact matches. So we can't redirect.Let's say that those listings 1 are globally unique ids. There is only one record for them in the database. When that listing is removed from shops 1 and 2 and later appears in shops 3 and 4, which shop do we redirect the original URL to? 3 or 4? We have only one choice and if we redirect to shop 4 the owner of shop 3 won't be happy and viceversa. We can add some reference in the JSON response that will look like 302 Location: /shop/4/listing/1 with a body including {"also_sold_by": [3]}' but again, why arbitrarily pick the main URL?
But if we have a URL like
/listings/1
we can return a reference to shops 3 and 4 in its JSON response. Everybody is happy.