Ask HN: What are good reads for designing APIs?

319 points by adnanh ↗ HN

100 comments

[ 4.7 ms ] story [ 119 ms ] thread
Check this one: Build APIs You Won't Hate - Phil Sturgeon
(comment deleted)
I second this. Great read if you want a better understanding of design your APIs.
I think if you have the time you should read SICP. It is not just about APIs, but my understanding of good APIs too increased a lot after reading this.
You should checkout the ØMQ guide. They do such a good job of first defining the problem space, and then establishing the design patterns they use to solve it.

The code examples almost become irrelevant because they make the patterns so clear.

http://zguide.zeromq.org/page:all

The Little Manual of API Design (Trolltech/Nokia): http://people.mpi-inf.mpg.de/~jblanche/api-design.pdf

How to Design a Good API and Why it Matters (Josh Bloch): http://static.googleusercontent.com/media/research.google.co... https://www.youtube.com/watch?v=aAb7hSCtvGw

Ugh. Can someone please point me to a good API designed by Josh Bloch? The handful I know of have all been a giant pain in my backside.

He keeps writing like he's an expert and I am still waiting for evidence to back that up,

You'll notice in this thread that (at 9 comments in) there are no repeated recommendations. If the question was "What are some good reads for learning about algorithms?" you'd probably see the same handful of books being praised by everyone.

Don't be overwhelmed by this though -- API design isn't an exact science. It's also very opinionated.

Personally, I would just start reading actual API documentation (GitHub is a great place to start -- their API is a joy to work with). Find things you like and don't like about it and try to figure out why those decisions were made.

Agreed. Find APIs you like to work with, think about WHY you like to work with them.

People get into really philosophical discussions around versioning, resource structure, etc... but in reality think about what makes you like the APIs you like over another alternative. Then think about what you would do to make it even easier to use/integrate with.

There also with some APIs is the tradeoff of easy integration and long-term flexibility.. things like that.

On that point, after you've read whichever of the many great recommendations you end up reading, don't get discouraged if you end up having to scrap the first several APIs you build. Part of the reason building APIs is so opinionated is that it's so difficult to do it well. Look at the API documentation for a bunch of different Google projects. Some of them are absolutely abominable. Others are quite good. Algolia has great documentation. When you get it wrong, you'll know. And that's all you really need to make sure you do better next time.
> I would just start reading actual API documentation (GitHub is a great place to start -- their API is a joy to work with)

This would be a good related subthread: links to API docs for what people consider to be both great, and terrible, APIs.

> This would be a good related subthread: links to API docs > for what people consider to be both great, and terrible, > APIs.

At least the great part has happened:

https://news.ycombinator.com/item?id=867972

There was one _much_ more recently (a week or two ago), but funnily enough the only one I can find is ~2.5k days old..

Related - best documented:

https://news.ycombinator.com/item?id=6224155

(comment deleted)
Definitely http://swagger.io/, supported by AWS as well.
Last time I checked(about 6 -7 months ago) swagger was quite messy. I ended-up with Hyper-Schema which proved simpler and better thought out.
This question is a little vague. My assumption is you mean a REST API.

This is a must watch and encapsulates good design and theory: https://www.youtube.com/watch?v=hdSrT4yjS1g

Good API design, if you are trying to learn from zero, comes from learning from good examples.

These are some of my favorites APIs by design

+ Stripe

+ Twilio

+ Slack

+ Stormpath (fd: I work here)

There is a lot of work that goes around the API design to make it a great API (examples, documentation, live samples, etc)

I recommend this physical book: 'RESTful Web Services Cookbook'. It walks you though the rationale behind HTTP APIs and progressively using more and more of HTTP's features to build a sophisticated but elegant API.

[1] http://shop.oreilly.com/product/9780596801694.do

I'd recommend the original REST thesis from Roy Fielding. It's a bit lower level than most of the other things mentioned here, but helps get a good feeling for what is important to achieve in a RESTful web service.
Definitely, it is a great read.
Times really do change very fast.

Many of the comments here take API to mean an HTTP exposed API (REST), but API stands for "Application Programming Interface"

It is much more generalized than APIs designed for HTTP consumption.

I think in this case we all had to infer context where none was provided, but you're right that the meaning has drifted.

These days the unit of work is very often some HTTP-exposed service rather than a platform library, or an interface to hardware or the like. But, I'll join your point and say there aren't too many (recent) generic resources about how to design non-HTTP APIs, since the idioms and patterns tend to be language-specific.

When doing my last bigger API i read every recommendation. Still learned a lot.

Everyone has different recommendations - dont get discouraged by this.

Make sure also to look into newer standards like JsonAPI if they are suitable - last time i tried to use it the tooling around it was still not strong enough and i decided to go w/ simpler custom api.

Assuming it has to be a restful api (vs graphql) and assuming you want to create an api for multiple kinds of clients (that's the the harder part) - Here my personal TL;DR:

- Autogenerate your docs with your tests

- Do versioning in URL (easier to route/cache/etc)

- Worry about caching (a lot)

- Personalized info only in isolated namespace, rest is fully cacheable

- Never embed personalized information (eg not `{ post: { user_has_commented: true } }`

- Never nest data (not `post: { author: { … } }` but reference only `post: {author_id: …}`)

- Embed referenced objects only by whitelist

- Never nest routes (not `/posts/343/comments` but `/comments?post_id=232`) filtering tends to become more complex

- Use public feedback tools (eg github issues) for your user questions/complains - so it can become searchable for people with similar problems

hth - happy to answer some of those in detail if useful

As said - highly subjective opinions - i am sure others might disagree w/ some of the points

Wouldn't most people disagree with: "not `/posts/343/comments` but `/comments?post_id=232`"?
This is one issue I struggled with in my last API design. I generally like the /comments?post_id=232 format but in many cases it's not intuitive. Especially when the sub-resource (i.e. comments) is only ever referenced in with a parent resource (i.e. posts).

This is a bit of a contrived example but with a structure like /comments?post_id=232, a developer might mistakenly assume comments are independent resources and not explicitly tied to posts when, in reality, there are can be no comments without posts. In this case, /posts/343/comments is much more intuitive.

The way we ended up handling it was forcing ourselves to limit sub-resources to a maximum of 1 level deep. So /posts/343/comments was allowable but something like /posts/343/comments/23/author was not allowed.

In my view, arbitrary limits are also not too intuitive :)

Problem with nesting is that very rarely your resource classes will make an acyclic graph. Even in your example '/posts/343/comments/23/author' resource class AUTHOR may be a child of either comment or the post itself. And if a user wants to view all posts by particular author? Intuitive use might as well be '/posts/343/comments/23/author/posts/123/comments' ad infinitum :)

Such problems can be solved by providing an endpoint for each distinct resource class and making it search provider. In your example case 3 endpoints are needed: /post, /comment, /author, all accepting other two as search parameters, e.g. /author?comment=123456 or /author?comments|id=123456 (inspired by FHIR).

my reasoning:

you end up w/ a lot of filters very quickly and related post id will be just one of them

also linking it to another resource usually involved expecting defaults (default ordering, default display, pagination etc)

if you stay "flat" this tends to be less of an issue when usecases become more complex

Probably yes, the former not only "looks better", but is possibly easier to use in a view. Just spawn two delegates with `$post_path` and `pathcat($post_path, 'comments')` and be done with it.

However, the argument being made here is that you may not want to expose post comments as sub-resource of a particular post, but rather as completely separate resource.

And there is a point to that. Suppose you serve the post content as a static content, and comments from some application (octopress + disqus style). The latter format allows you to route anything matching `^/posts/(.*)` directly at web server level without touching application server at all. If you decided to use the former, then your infrastructure becomes dependent on your API structure. Not very nice :)

Never nest data (not `post: { author: { … } }` but reference only `post: {author_id: …}`)

I'm creating a huge API on my dayjob and we have nested A LOT. So many levels of nesting, the responses have become too big.

Yet, the clients refuse to call additional endpoints and always insist on this. And it _does_ make sense for them to make 1 call and retrieve all information they need.

How does everyone handle this on REST?

(I know GraphQL is a solution.I'm wondering how people use REST API's)

I am not familiar with GraphQL and my APIs are serving only a handful of people. I do have one suggestion: write a really really good client library for your user.

This eventually can become the basis of your test harness. In fact, every time I write my tests, I end up writing a client library...

If you must, serve up the authors as an array that is a sibling of the posts array. So if you have 100 posts written by only 3 authors, you have:

  { 
    posts: [ /* 100 records */ ],
    authors: [ /* 3 records */ ] 
  }
Each post references the author by ID only and all required data is sent in one API call.
Even better: make authors be a map of ID to author so you can get fast lookup by ID when you need it.
- Never embed personalized information (eg not `{ post: { user_has_commented: true } }`

Until you hear from an iOS developer that it is a huge pain trying to figure out if the user has left a comment on an item or not and including this little flag would save tons of time.

client lib code is never the issue. usually scaling or breaking contracts is

you can just add it as separate key (similar how you embed eg user objects)

``` posts: {…} interactions: { commented_on_posts: [1212,12,1212,121] } ```

btw most people over estimate this problem the _total_ amount of user interactions is usually very small

in almost all cases you could download it once at boot for the user.

I'm going to take the pretentious and predictable* route here and recommend The Design of Everyday Things. It's not about REST APIs or whatever particular instance you're after, but it is more philosophical about what APIs are.

*Like a freshman preaching Rand

I really liked Building Stripe’s API and it’s sequel, Move fast, don’t break your API:

http://amberonrails.com/building-stripes-api/

http://amberonrails.com/move-fast-dont-break-your-api/

The amount of versioning that Stripe does makes nervous. Is that a good practice?
(comment deleted)
As long as they adhere to the second part of their slogan, and "don't break [the prior versions of] [their] API", it's great.
Personally the versioning and backward incompatibility don't concern me in themselves, as long as integrations with older API versions are also fully supported indefinitely and there is a clear and safe path to upgrade.

In Stripe's case, I normally find an initial integration is a better experience than most payment services. The API is reasonably designed and well documented.

However, I also find Stripe integrations are effectively impossible to maintain or update over time. Older API versions aren't documented anywhere I can find, only the current one. There are decent changelogs with warnings of incompatibilities, but you can't write an automated integration test suite. I've never found any documentation that explains how their versioning actually works, what is affected, and how to revert if you update and there's a problem.

In practice, that means older versions of the API aren't fully supported indefinitely, nor is there a safe, systematic path to manage an upgrade to a newer API version. For that reason, we normally treat Stripe integrations as write-only code, where once you've got something written, tested and into production, it's never touched again (short of a serious security issue or the like, obviously).

Why are they versioning using dates? I know you probably wouldn't want to deploy a public API change more than once per day, but if version numbers are equally good why not give yourself the option? It also could lead to confusion about which API is newest due to UK vs US date formatting whereas a version number is unambiguous.
When I saw this thread title, I clicked and immediately Ctrl+F'd for 'amber.' Great read in general.