Live data from Hacker News

Everything I know about good API design

seangoedecke.com

151–160 of 168 posts

Re: Everything I know about good API design

#151
post #82

Earlier quoted context omitted.

I mean… is adding an OAuth layer in 2025 adding that much complexity? If you’re scripting then there’s usually some package native to the language, if you’re using postman you’ll need to generate your authn URL (or do username/passwords for client ID/secret). If you have sensitive resources they’ll be blocked behind some authz anyway. An exception I’ve seen is access to a sandbox env, those are easily generated at th…

No, I'm just saying that an OAuth layer isn't really adding much benefit when you either use an API key to obtain the refresh token or the refresh token itself becomes a long-term secret, not much better than an API key. Some way to break out of the "shared secret" model is needed. Mutual TLS is one way that is at least getting some traction.

Refresh tokens aren’t necessarily long lived, you can force the client to exchange for another refresh token.

Re: Everything I know about good API design

#152
post #146

Earlier quoted context omitted.

> While the author doesn't seem to like version based APIs very much, I always recommend baking them in from the very start of your application. You don’t really need to do that for REST APIs. If clients request application/vnd.foobar then you can always add application/vnd.foo.bar;version=2 later without planning this in advance.

The problem is with parameters (or headers) you are still stuck with same API schema (you cannot rename it, etc). But thanks to versions, in my job we renamed old APIs like /v1/oauthApple and /v1/oauthGoogle to /v2/login/oauth/apple and /v2/login/oauth/google, looks so much better.

> The problem is with parameters (or headers) you are still stuck with same API schema (you cannot rename it, etc).

That doesn’t make sense. The whole point of creating a new version is to change the schema. And what do you mean “rename it”?

> But thanks to versions, in my job we renamed old APIs like /v1/oauthApple and /v1/oauthGoogle to /v2/login/oauth/apple and /v2/login/oauth/google, looks so much better.

Wait, by schema do you mean URL structure?

You’re looking at this backwards. The benefit of using headers is that you can keep the same URL. In a REST API, the URL is the primary key. If Client A holds a copy of /v1/foo/1 and Client B holds a copy of /v2/foo/1 then as far as HTTP and REST are concerned, those are two different resources and the clients cannot interoperate. If Client A holds a copy of /foo/1 in application/vnd.foo;version=1 format and Client B holds a copy of /foo/1 in application/vnd.foo;version=2 format, then those clients have the same resource and can interoperate.

But if you want to change your URL structure, you can do that too. There’s nothing stopping you from moving /oauthApple to /oauth/apple, you don’t even need a new version to do that – just change the link.

Re: Everything I know about good API design

#153

> Instead of using OFFSET, the query becomes WHERE id > cursor ORDER BY id LIMIT 10 Wait. Surely "ORDER BY id OFFSET 20 LIMIT 10" works about the same as "WHERE id > cursor ORDER BY id LIMIT 10", if "id" is indexed?

no, because it has to count the amount of matching rows preceding the offset rows to determine the offset, i.e. iterate over all preceding rows. The cursor provides a starting point for the offset so in this instance it's not necessary. https://use-the-index-luke.com/sql/partial-results/fetch-nex...

It only has to count if the index doesn't store the amounts of records in the "chunks" it manages, does it? I'm pretty sure B-trees do actually store the sizes of the subtrees.

Re: Everything I know about good API design

#154

This is great. One thing I would add: The quality of the API is inversely correlated to how difficult it is to obtain API documentation. If you are only going to get the API documentation after signing a contract, just assume it’s dismally bad.

Sometimes you get the docs emailed in a password protected spreadsheet :')

Re: Everything I know about good API design

#156
I've always been a staunch opponent of using `/v1/` kind of element in the URL. I understand its function and the convenience it allows for "versioning", but I grew up with HTTP and I always liked how it insisted the URL is well, the _location_ of the _resource_, and `/v1/` just muddies things up being in the URL -- it's obviously not a version of the resource that `/v1/` would be indicating, but of the API implementation, which is the first telltale sign that it's an architectural "blunder", and these compound in my experience, always and invariably given enough time.

If the consumer wants to consume a specific version of the API, the means to do so can be implemented with an alternative domain name, or -- even better (who wants to maintain alternative domain names) -- with a request _header_, e.g. `X-API-Version: v1` (or another one, perhaps a standardised one).

In any case, the `/v1/` thing is something of cargo cult programming -- I remember someone proposed it a good while ago, and it's been adopted since without much afterthought, it seems. It doesn't make sense to debate pros and cons of REST/HATEOAS if your resource identifier scheme is poorly designed, IMO.

Re: Everything I know about good API design

#157

The reminder to "never break userspace" is good, but people never bring up the other half of that statement: "we can and will break kernel APIs without warning". It illustrates that the reminder isn't "never change an API in a way that breaks someone", it's the more nuanced "declare what's stable, and never break those".

In software engineering the statement "interfaces, not implementations" has been used for a long time (certainly at least Robert "Uncle Bob" C. Martin started teaching), which is a generalization on the "we don't break userspace". In essence it cooks down to declaring an interface without announcing or depending on the implementation. With OOP languages like C++, a code base would aggressively use interfaces as types, never concrete class types (which implement the interface), so that it can make it easier to reason about how and whether the program behaves when one implementation of an interface is swapped for another.

With Linux, which is a C codebase by and large, they load and pass pointers to structures to kernel procedures which can do as they please -- as long as the documentation on said structures (which usually says which fields and how are retained with which values and so on) remains unchanged. That's their "object oriented programming" (yeah, I know Linus would likely have hated the comparison).

Re: Everything I know about good API design

#158

Earlier quoted context omitted.

> While the author doesn't seem to like version based APIs very much, I always recommend baking them in from the very start of your application. You don’t really need to do that for REST APIs. If clients request application/vnd.foobar then you can always add application/vnd.foo.bar;version=2 later without planning this in advance.

If you use something like an OpenAPI generator and want to have different DTOs in your version 2, then you cannot do what you suggested.

I've been using OpenAPI for years with multiple versioning types (header based, content negotiation + media type based) and haven't had issues across Java, Typescript or Go with generating the right code for it

Re: Everything I know about good API design

#159
post #88

Earlier quoted context omitted.

Even if the kernel doesn't break userspace, GNU libc does, all the time , so the net effect is that Linux userspace is broken regardless of the kernel maintainers' efforts. Put simply, programs and libraries compiled on/for newer libc are ABI-incompatible or straight-up do not run on older libc, so everything needs to be upgraded in lockstep. It is a bit ironic and a little funny that Windows solved this problem a co…

You're describing 2 completely different things there. If your program is built to require myfavoritelibrary version 1.9, and you try to run it against myfavoritelibrary 1.0, no shit it doesn't work. Glibc is no different than any other in this regard. If your program is built to require myfavoritelibrary version 1.0, and you try to run it on myfavoritelibrary 1.9 ... glibc's binary compatibility story has been very…

> glibc's binary compatibility story has been very good since the release of 2.2 or so, way back in 2000

It has been better than most but they recently broke loading libraries that declared they need an executable stack (even if the library never used it) and there doesn't seem to be a plan to actually fix the backwards compatibility issue.

Re: Everything I know about good API design

#160
post #134

Earlier quoted context omitted.

You'd need to distribute ld.so also, otherwise you'll run into ld/libc incompatibilities.

Sure.

... and ld.so needs to be at a specific absolute path so you can't just distribute it along with your application, you need to use an actual container for this.
Post reply on HN