Live data from Hacker News

How to Design Better APIs

r.bluethl.net

141–150 of 238 posts

Re: How to Design Better APIs

#141

Sometimes, I feel that we ought to have a simple protocol, on top of HTTP, to simply do remote procedure calls and throw out all this HTTP verbs crap. Every request is a http POST, with or without any body and the data transfer is in binary. So that objects can be passed back and forth between client and server. Sure, there is gRPC, but it requires another API specification (the proto files). There I said it. HTTP Ve…

As someone who has spent a decade working with APIs, I 100% agree. The use cases that are a good fit for “RESTful” APIs pale in comparison to those that would benefit from RPC. What is the point of having your client translate an action to some operation on a document (read or write), only to then have your server try to infer what action was intended by said document operation. It pains me that this article doesn’t…

+1, each time I return 404 for an object which is not found in the DB, the customer gets a red error message in their UI as if something failed more severely than an object being unavailable, and the metrics believe something is unavailable.

I bit my fingers every time I have mapped HTTP verbs and neither return codes, to REST verbs and codes.

Also, error codes at the API level often need a remapping when used in user context, for example if the OAuth token expires, we don’t say it the same way for an action of the user (then it’s mandatory) than when displaying data passively (in which case it shouldn’t be too red because the user may not care).

Re: How to Design Better APIs

#143
The author has a poor understanding of HTTP and adjacent standards. I find it is so insufficient that he should not dispense advice yet, he must learn much more, especially about the essential yet completely unmentioned parts of a Web system: media types, hyperlinks, Link relations, cacheability. Critique:

2. ISO 8601 is a shit show of a standard. Last time I surveyed, there was not a single compliant implementation in the world. Recommend "Date and Time on the Internet: Timestamps" http://rfc-editor.org/rfc/rfc3339> instead. This standard is public/not encumbered by stupid copy fees and is restricted to a much more reasonable profile that can actually be implemented fully and in an interoperable fashion.

4. Use `OPTIONS ` instead. http://rfc-editor.org/rfc/rfc7231#section-4.3.7>

5. This goes against the design of the Web. Do not version URIs. If the representation changes and is not backward compatible, change the media type instead, e.g. by adding a profile http://rfc-editor.org/rfc/rfc6906>. You can serve multiple representations on the same URI and vary on the request headers (e.g. Accept-).

6. HTTP already contains a customisable mechanism. Use the standard header http://rfc-editor.org/rfc/rfc7235#section-4.2>, not the custom header `Api-Key` which is not interoperable.

7. "Don't use too many [HTTP status codes]": why? This opinion is not backed up with an explanation. The correct advice is: use as many status codes as arise from the requirements. `422 Unprocessable Entity` is useful in nearly every Web system.

8. What does "reasonable" mean? This lacks an explanation.

10. Use application/problem+json http://rfc-editor.org/rfc/rfc7807> instead.

11. "It's a good idea to return the created resource after creating it with a POST request": why? This opinion is not backed up with an explanation. If you follow this advice, the user agent cannot programmatically distinguish between a representation reporting on the requested action's status, and the representation of the newly created resource itself. Use the Content-Location header http://rfc-editor.org/rfc/rfc7231#section-3.1.4.2> to make that possible, use the Prefer header http://rfc-editor.org/rfc/rfc7240> to give the user agent some control over which representation to respond with.

12. "Prefer PATCH over PUT": disagree, ideally you offer both since there is a trade-off involved here. The downsides of PATCH are not mentioned: the method is not (required to be) idempotent meaning it becomes moderately tricky to keep track of state, and the client is required to implement some diff operation according to the semantics of the accepted media type in the Accept-Patch header which can be difficult to get right.

13. Missed opportunity to advertise the OPTIONS method and related Accept-Post https://datatracker.ietf.org/doc/html/draft-wilde-accept-pos...> / Accept-Patch http://rfc-editor.org/rfc/rfc5789#section-3.1> headers. A representation of a specific media type can self describe with the "type" Link relation http://rfc-editor.org/rfc/rfc6903.html#section-6>.

14. Don't mix data with metadata. Use the Range header http://rfc-editor.org/rfc/rfc7233> and next/prev Link relations https://webconcepts.info/concepts/link-relation/next> instead.

15. Instead of complicating both server and client, the better idea is to simply link to related resources. We are not in the 1990s any more. A well written Web server offers HTTP persistent connections, HTTP pipelining, all of which make round-trips cheap or in the case of HTTP/2 server push, even unnecessary. Benchmark this.

1. + 9. betrays a weird obsession with naming. The advice is not wrong, but it shifts attention away from the genuinely useful areas that benefit much more from a careful design: the media types, the hyperlinks between and other hypermedia mechanisms for traversing resources (forms, URI templates). If an inexperienced programmer follows the advice from the article, he will the idea to spend lots of time mapping out resources in the identifier space and methods and possible responses for documentation purposes. This is both a waste of time because the single entry point is enough and the rest of the resources can be reached by querying the server via OPTIONS and traversing hyperlinks etc., and dangerously brittle because of strong coupling between the server and the client.

Re: How to Design Better APIs

#145
post #46

Earlier quoted context omitted.

It might be to formal for your use-case, but there is a standard defined for error responses in RFC 7807: https://datatracker.ietf.org/doc/html/rfc7807

Wow, I had never seen an API with errors at this level of detail… I feel lucky when they at least use sane status codes instead of always giving back 200 and a maybe-json-maybe-plaintext-maybe-empty body… I’d love to hear from anyone who has encountered APIs in the wild that actually implement this standard!

I've used both Problem Details and JSONAPI errors[0] which are basically the same idea (and I've used them plenty outside of JSONAPI-proper APIs). In both cases if you have a decent error-handling middleware there should be not much difference than outputting any other kind of errors.

One thing to keep in mind re. "maybe-json-maybe-plaintext-maybe-empty" responses is that the more complex your errors, the more likely the error handling encounters an error. If you're trying to send back some JSON or XML but it fails it's usually better to at least shove out a line of plain text and hope it reaches a human than to mask the real error with a second fallback in the "right format" but with unhelpful fixed content.

[0] https://jsonapi.org/format/#error-objects

Re: How to Design Better APIs

#146
post #96
post #72

Earlier quoted context omitted.

Those are possible, but ugly solutions. Two cleaner ones are either depracate and remove the v1 api altogether, or when inserting a record to the database from the v1 api, use a default dummy value for avatar_url.

Yes, and definitely favour the deprecation. Treat web APIs like any interface - have minor and major versions, deprecating then dropping old versions.

Although I agree with the comments above, adding a field is also a breaking change, even with a default value. Especially prone to this is any openApi client (speaking from experience ...). Maybe filtering it out would be an actual solution without breaking anything. An API update shouldn't need to update my implementation because it's not working anymore, in that case it's a breaking change and a major version bump.

Re: How to Design Better APIs

#147
Something I wonder is how to design search/list endpoints where the query can be long (a list of IDs, ex: /users?ids=123e4567-e89b-12d3-a456-426614174000,123e4567-e89b-12d3-a456-426614174001,123e4567-e89b-12d3-a456-426614174002,...), so long that it can exceed the url max length (2048), after 50 UUIDs, you can quickly exceed that length, so GET is not ideal, so which method, SEARCH with a body? POST with a body?

Re: How to Design Better APIs

#148
post #132

Earlier quoted context omitted.

You appear to be referring to a database cursor. It is quite simple to implement pagination in the application layer using a unique record identifier (primary key or ULID or ...) as an anchor for the navigation. From that unique ID, we can then fetch the previous `n` or the next `n` records, depending on the direction of the navigation. This way, the server remains stateless, since the anchor (possibly sent as an enc…

What if that particular unique ID is deleted right before the client requests the next page?

Pagination only makes sense in the context of an ordered collection; if there is no stable sort order then you can’t paginate. So you identify the last record seen with whatever fields you are ordering by, and if the last record has been deleted, then it doesn’t matter because you are only fetching the items greater than those values according to the sort order.

Anyway, there is plenty of documentation out there for cursor-based pagination; Hacker News comments isn’t the right place to explain the implementation details.

Re: How to Design Better APIs

#149

Something I wonder is how to design search/list endpoints where the query can be long (a list of IDs, ex: /users?ids=123e4567-e89b-12d3-a456-426614174000,123e4567-e89b-12d3-a456-426614174001,123e4567-e89b-12d3-a456-426614174002,...), so long that it can exceed the url max length (2048), after 50 UUIDs, you can quickly exceed that length, so GET is not ideal, so which method, SEARCH with a body? POST with a body?

I would go with POST with a body in that case, where I interpret is as a "new search item" and use GET to scan through results, if there are many results available. I don't think I've needed something like this more than once or twice though. From users perspective, asking information on specific 50 items at once is not something commonly done.

Re: How to Design Better APIs

#150

Something I wonder is how to design search/list endpoints where the query can be long (a list of IDs, ex: /users?ids=123e4567-e89b-12d3-a456-426614174000,123e4567-e89b-12d3-a456-426614174001,123e4567-e89b-12d3-a456-426614174002,...), so long that it can exceed the url max length (2048), after 50 UUIDs, you can quickly exceed that length, so GET is not ideal, so which method, SEARCH with a body? POST with a body?

There was a new RFC published a few months ago to address this use case. It defines a new HTTP method QUERY, which is defined to be safe & idempotent like GET and explicitly allows a request body like POST. See https://www.ietf.org/id/draft-ietf-httpbis-safe-method-w-bod...
Post reply on HN