Hacker News new | past | comments | ask | show | jobs | submit login
The Elements of APIs (2021) (johnholdun.com)
165 points by luu 5 days ago | hide | past | favorite | 30 comments





While I enjoy this kind of resource, I miss some explanation about why they recommend doing things that way.

When championing good practices like these within a team, they are often somehow challenged, and "do it because I say so" is a weak answer.

Also, not understanding why these are good practices is a good way to end up doing cargo cult architecture.

Edit:

> Some of the choices I’ve listed so far may seem arbitrary, and they are! You and your team should make aesthetic changes if you want to, but I urge you to define them on a system level. If you can’t agree on something that is subjective, I recommend that you just do what I said, because it doesn’t matter and this way of doing things is already written down.

That's a very good point.


I think the same applies to laws. You can't get information on why a law was passed, and many of them end up seeming odd or even counterproductive.

anyone interested in changing that? I'd chip in

I founded an integration startup, so my entire life right now is figuring out how to connect to and normalize an endless number of APIs.

His advice here is spot on. You can’t underestimate how inconsistent the API landscape is, and he’s right that the fancier ones are harder to use.

Some notes:

- Surprisingly less than 5% of APIs reliably use PATCH.

- I’m not sure it’s a good idea to return an Object under data for single resources, and an array when requesting multiple resources. Theoretically it sounds better, but in the end it makes it harder to consume.


I would wrap a collection always into an object, makes evolving the api in a backward compatible way easier. I am still maintaining a few endpoint where I choose differently, and have cursed younger me more than once for it.

> Surprisingly less than 5% of APIs reliably use PATCH.

When I started building my companies main api about 8 or 9 years ago, PATCH was nowhere to be seen. The advice was PUT for creating and POST for updating. some resources had it the other way around, but it was always PUT and POST. I am surprised to see PUT has now been deprecated in favour of PATCH, but I fear that making a new version of the api just to swap 2 methods is unecessary.


The difference between PUT and PATCH, at least in convention, is that PUT tends to assume you are providing the entire object of whatever it is you're updating, and PATCH will accept a partial object and combine it with whatever's already there.

A common difference between POST and PUT is that POST is often used for non-idempotent actions (creating a resource), and PUT for idempotent actions (updating or upserting a resource). Of course this is also purely convention; nothing in the implementation of either enforces that usage


I've seen both PIT and PATCH used side-by-side, where put is a complete replacement and patch is where to, well, patch only what you need, sometimes using the JSON-patch format.

> I am surprised to see PUT has now been deprecated in favour of PATCH...

Wait, what? Is that an "official" thing anywhere in regards to the method itself, or just an observation with the APIs that you've worked on?


Pleasently surprised. I was taken aback when I read the "by John Holdun" line and suspected shameless self-promoting, but the site is really well written and structured.

One thing though: I rarely see PATCH and that's because clients might send "identical data" as POST multiple times. You have to know what they want to do in business logic anyway and clients need to understand your response.

Sure you can give them "409" or "303" back, but who expects those? Which one do you choose? You have to document it anyway? Or do you return 400 { error: "Already exists" }?

This isn't well defined in the HTML spec so implementing PATCH feels useless. It's easier to accept that you'll receive invalid data on POST and you'll have to return custom error codes.


GET and (sparse upserting) PATCH is all you need for a multiplayer API. Arguably GET is spurious as you could do an empty PATCH to do a read but you generally want a cache infront of GET that exploits etags

What format do you provide for in your Accept-Patch header that accepts empty inputs?

An empty JSON object for a JSON merge patch, not an empty body

> You use GET for retrieving, POST for creating, PATCH for updating, and DELETE for deleting.

Surely that should be "PUT for creating, POST for updating"? PUT is a standard verb, defined in every version of the HTTP/1.1 spec starting since RFC 2616, after all, unlike PATCH which is still just a "proposed" standard.


For reference, the author touched on their reasoning at the bottom of "Many nouns, few verbs"

https://johnholdun.com/apis/verbs/


Huh, TIL that PATCH isn't an accepted standard: https://www.rfc-editor.org/info/rfc5789

14 years on, I wonder what the hold up is.


Note that this is about web APIs specifically.

> Here are my priorities, in order of importance. I think these should be your priorities too.

I might perhaps consider adding “it’s secure" to that list...

(And if I might do some shameless self-promotion: https://www.manning.com/books/api-security-in-action)


Contains similar advice to resource-oriented design and Google's API Improvement Proposals [1]. I've been pretty happy with resource-oriented design--makes for a very uniform API and avoids much of the bike-shedding over decisions like the filter [2] and pagination format.

[1]: https://google.aip.dev/121

[2]: https://google.aip.dev/160

> Your GETs support a filter query parameter, which works for every attribute of the resource being retrieved.

That's not easy in practice. You'd have to filter the entire collection on the server or database. For large tables, you'd need to index attributes and build the appropriate predicate, which can be challenging for normalized tables.


You know what's not discussed here, or in pretty much any discussion of APIs that I can recall?

Enabled vs. disabled. There doesn't seem to be any priority given to communicating to the API client what parts are enabled or disabled at any given point in time.

Why?


I like it, but I wonder what TK means. It must be something like a todo, because it's always used to mark incomplete sections. And since the date says 2021, I'm also left wondering when an update could come...

It is a convention from journalism that is similar to TODO: https://en.wikipedia.org/wiki/To_come_(publishing)

A "todo?"

I'd speculate about a little dog, but that'd be "Toto."


This was a great read but one thing I'd like to see is some examples that, for the most part, follow these conventions. Any suggestions that I can dig through as a reference point?

In terms of taking an openapi specification first, then generate server/client from that approach, I have a project that focuses on this for nodejs koa and fetch/axios: https://openapi-code-generator.nahkies.co.nz/overview/about

I plan to eventually incorporate guidelines on writing good openapi specifications, and general design patterns for API.

Very much agree with most of the points in the article, such as returning objects over flat array's, supporting partial updates via PATCH.

I'm less convinced by exclusion of PUT (sometimes a client generated id / idempotent create is useful), and ability to filter collections on all fields (sounds inefficient).

Look forward to returning to this when some of the to-dos have been filled out


Something not mentioned that I've run into frequently is the need for batched operations (i.e. not single document at a time posting).

As volumes increase the overhead of single document updates in the backend systems+database slows things down. Multiple connections can help but frequently there is throttling in the destination system.


Re: https://johnholdun.com/apis/rest/

> When I say “REST,” I don’t mean when to use GET vs. POST (although that’s also important). What I mean is that you should read Roy Fielding’s dissertation, Architectural Styles and the Design of Network-based Software Architectures, where Representational State Transfer was coined. Not once in its definition does he mention HTTP verbs!

Something that I still don't understand, is how HATEOAS relates to OpenAPI spec. Quoting Roy T. Fielding himself (https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypert...):

> API designers, please note the following rules before calling your creation a REST API:

> [...]

> A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace. Instead, allow servers to instruct clients on how to construct appropriate URIs, such as is done in HTML forms and URI templates, by defining those instructions within media types and link relations. [Failure here implies that clients are assuming a resource structure due to out-of band information, such as a domain-specific standard, which is the data-oriented equivalent to RPC’s functional coupling].

> [...]

> A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the API). From that point on, all application state transitions must be driven by client selection of server-provided choices that are present in the received representations or implied by the user’s manipulation of those representations. The transitions may be determined (or limited by) the client’s knowledge of media types and resource communication mechanisms, both of which may be improved on-the-fly (e.g., code-on-demand). [Failure here implies that out-of-band information is driving interaction instead of hypertext.]

Wouldn't that mean that using OpenAPI spec automatically means something is not a REST API, but instead it's just an RPC?

Not saying that's good or bad, just contrasting this with OP's recommendation against using "an RPC-based interface" while also quoting Roy on REST.

(Edit: Added more context.)


Having learnt web development by coding Ruby on Rails, I wouldn't design an API any other way.

It was only after I left Rails land that I discovered that these great patterns were not used everywhere.

I quickly returned to Rails.


If you like JSONAPI I maintain a 100% JSONAPI compliant headless cms

https://github.com/daptin/daptin


IMO the book title be 'The Elements of Web APIs'



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: