5 ms·
This is nice. It reminds of how miserable my life is. — Which HTTP code I should return for my API? I already used 404, 403, but I need another one. Damn, HTTP
by alganet 3y ago
This is nice. It reminds of how miserable my life is.
— Which HTTP code I should return for my API? I already used 404, 403, but I need another one. Damn, HTTP is so old and it makes no sense.
— You can't use HTTP codes like that Bob, they're not a free choice. They're for the protocol, not for your app.
— Let's look at the list. Hm... "412 Precondition Failed". Hey, it sounds nice. It fits to my use case. I'm gonna document it. It means the account is out of balance.
— What is this garbage? Please read the spec. This is going to make our API gateways, CDNs, everything go crazy. Can't let you move on with this PR.
— Look. I documented it, made an enum with the code, it's clean. I'm an experienced REST developer.
— It... it doesn't work like that Bob. Please, read the spec.
— Hey, got enough approvals, "412 Account Out Of Balance" it is! It passes the tests.
For each dev that knows proper HTTP, there's 10.000 Bobs.
- esafak 3y agoWhich article would you ask Bob to read to learn the right way to do it?
- alganet 3y agoBob has since moved on to crypto, leaving the cache invalidation eternally crippled. New Bob decided that everything is useless and wants to rewrite the whole backend using a faster language.
- mozey 3y agoHave to admit I've never used this code, and didn't know what it was about. Quickly read up about it. So ETag is a hash of the resource. You must provide it with requests that modify the resource. If your hash doesn't match the server hash, then 412 Precondition Failed is returned? https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412 https://www.rfc-editor.org/rfc/rfc9110#status.412 https://www.rfc-editor.org/rfc/rfc9110#status.412
- alganet 3y agoYou can provide all sorts of conditions using HTTP headers such as "If-Match", "If-None-Match", "If-Modified-Since", "If-Range", etc. The server can chose an HTTP code to indicate some sort of cache invalidation signal. 304 means "you're good, your cached version satisfies the conditions" 412 means "you're not good, your cached version does not satisfy the conditions" 412 usually applies to modifications, but it could be for reading too, in the case of ranged requests (getting a specific range of bytes from a large representation). See the "Range" header. These are all interconected. The headers, the codes, etc. They are very useful for caching and can save a lot of bandwidth. Browsers and CDNs use them extensively. Server-to-server communcation could use them as well, but I haven't seen popular implementations (let's say, a web framework that provides abstraction over these mechanisms).
- alganet 3y agoAlso, other popular HTTP codes have cache implications. For example, 404 implies "No indication is given of whether the condition is temporary or permanent". Cache invalidation headers don't apply to this code because a 404 means there's something about the _resource_ and not only the _representation_ that could not be found. That client cannot cache the 404 result, not even for a fraction of a second. 404's brother 410 implies "This condition is expected to be considered permanent.". A client that gets a 410 can cache that result, never needing to reach the server again. It means it's gone forever. That client can decide to never look up that URI again. Very often, "400 Bad Request" is the best HTTP you can use if you are not sure what to use. Then, describe what the error means using other HTTP components and/or the response body. HTTP can be very simple. GET (ask for a representation) and POST (send a representation) as methods only. 200 (success), 400 (client error) and 500 (server error) as response codes only. It's the best way to start, then move to more elaborate protocol features as you learn.
- troupo 3y agoHTTP decision diagram: https://github.com/for-GET/http-decision-diagram https://github.com/for-GET/http-decision-diagram and Know Your HTTP Well: https://github.com/for-GET/know-your-http-well https://github.com/for-GET/know-your-http-well
- AdieuToLogic 3y agoFor the shortcoming of conveying errors strictly through HTTP status codes, consider: RFC-7807, Problem Details for HTTP APIs[0] From the introduction: HTTP [RFC7230] status codes are sometimes not sufficient to convey enough information about an error to be helpful. While humans behind Web browsers can be informed about the nature of the problem with an HTML [W3C.REC-html5-20141028] response body, non-human consumers of so-called "HTTP APIs" are usually not. This specification defines simple JSON [RFC7159] and XML [W3C.REC-xml-20081126] document formats to suit this purpose. They are designed to be reused by HTTP APIs, which can identify distinct "problem types" specific to their needs. HTH 0 - https://datatracker.ietf.org/doc/html/rfc7807 https://datatracker.ietf.org/doc/html/rfc7807
- stingraycharles 3y agoMy coworkers insisted on always returning 200 and having the status code in JSON. At least at that point it’s clearly not HTTP anymore, and it’s better than pretending to be compliant like your Bob. But something dies inside me whenever I have to work with it.
- peoplefromibiza 3y agoThat's exactly what SOAP and GraphQL do, they always return 200
- ahepp 3y agoIsn’t SOAP theoretically “transport agnostic”. It doesn’t leverage http verbs either.
- peoplefromibiza 3y agoCorrect. theoretically SOAP could work using SMTP/POP or whatever transport mechanism.
- alganet 3y agoI'd say returning 200 for all successes is reasonable if the responses are simple. Returning 200 for an error makes no sense. Having 400s and 500s is the simplest way to have observability over protocol behavior (think logs, error rates, etc). If you use all 200s, you'd have to re-implement observability by yourself, so you lose simplicity that you gained by ignoring those statuses. It's the same thing with caching stuff. You could implement those outside the protocol, but then you'd be writing your own protocol (trying to be smarter than decades of engineering efforts).
- sa46 3y agoI prefer to treat HTTP as a transport layer. A 200 means the transport succeeded but doesn’t indicate that the RPC succeeded.
- knallfrosch 3y agoAh, the eternal battle. Do you return an HTTP error code if the problem lies in the domain? I think I've seen everything on that scale. On one end: { status: 200, error: InsufficientFunds } On the other: - "let's use 409, it perfectly fits our use case" + "but we don't have useful error codes for all the other domain errors."
- alganet 3y agoStatus 200 with "InsufficientFunds" can be correct. Let's assume your resource is "/account/1234/withdraw-availability" It's a hypothetical endpoint you can GET to know if you can withdraw money. You hit it, and the request is sucessful (the server understood and will inform you whether withdrawaw is available or not). Let's assume your resource is "/account/1234/withdraw" This other hypothetical endpoint you can POST a request for money withdraw. Returning 200 here means the server understood and processed your request, so a 200 that does not withdraw makes no sense. The same endpoint could also return a success "201" accepted (the server understood the request, but it is not processed yet). In the body, there would be a link for "/withdrawaws/48957987593845983475/status", a resource specific to this future processing, which you can GET later (maybe 1ms later, within the same socket). This GET could also return a 200, saying that such withdrawaw was not possible (the server sucessfully understood the request and will inform you about the status of the withdrawaw). For this modeling stuff, the Roy Fielding dissertation about REST is more enlightening than the spec. The spec is still needed though.
- lstamour 3y agoOne more example, if your request contains a batch of operations, you generally have to return 200, or maybe 204, if it was successfully received and should not be retried in full. In the response body, you might give other response codes for specific errors or failures to retry in a new request. So it can easily make sense to return 200 when there are e.g. partial failures and partial success and the request was properly formatted, authorized and acted upon.
- quectophoton 3y agoTo be fair, to me HTTP looks like: - The first line of an HTTP request has its own format (space-separated-ish), and mixes method and URL path. - The URL path in that first line has its own format and weird escaping, and mixes one path with zero-or-more key value pairs. - The headers have their own format. - The body has its own format. Most (all?) HTTP libraries for clients and servers abstract all that mess away into a neat object that could be easily represented in JSON (or bencode if you want something simple-ish while supporting binary data), but it's like using a nice program while knowing it's written in C[1]. Of course, JSON has its own problems, but in that case at least we only need to deal with the problems of one format that supports with proper nesting, not 4 different weird formats masquerading as one. [1]: Disclaimer, I don't like Rust, so don't take this as a RIIR thing. EDIT: To be clear, I don't agree with how Bob misuses HTTP in your example. I just find it sad that we're locked into this weirdly complex protocol.
- alganet 3y agoURLs are opaque: - https://www.w3.org/2000/12/drm-ws/pp/connolly/slide8-0.html - https://www.w3.org/DesignIssues/Axioms.html#opaque For the protocol, any structure or meaning in URLs is irrelevant. It is one more example of mixing application domain with protocol level stuff. HTTP header format goes back to ARPA times. Email reused them, so did HTTP. - https://datatracker.ietf.org/doc/html/rfc822#section-3.2 https://datatracker.ietf.org/doc/html/rfc822#section-3.2 Many of these choices are there for backwards compatibility and reuse. I can totally understand why. An HTTP body has no predefined format. It can be anything. It can be a stream (HTTP into WebSocket upgrade, for example). It is the media type that defines how the body should be interpreted. HTTP requests are meant to be used before they are fully transmitted, and are formtted in a way to leverage socket communication. JSON, on the other hand, needs the whole document to be read before it can be safely interpreted. HTTP has more moving parts, but it also does so much more. These two aren't even comparable, they're not in the same layer. I understand the urge to "improve" on all of this "legacy", however, one must consider how much was built upon these standards and if there's anything real to gain by changing them.
- HdS84 3y ago
- vbezhenar 3y agoI'm using 400 for everything, to the hell with it.
- rednafi 3y agoYeah, same. Otherwise, domain and protocol error codes become a confusing mess.
- HdS84 3y agoThe problem ist that there are multiple status codes which should be used (400, 422, 500 etc). But others are massive footguns. Would have been better to have a "technical" status code and a "application status code", but hey that would have required better engineering.
- alganet 3y agoRTFM. It's all there. Not a blogpost, not a cheat sheet, the manual (in this case, the spec). Yes, it takes a while. Anything can be a footgun if misused.
- rpastuszak 3y agoI’d just send Bob a link to https://http.cat/412 https://http.cat/412 and hope they get too distracted to continue the conversation.