this post was submitted on 30 Aug 2024
6 points (100.0% liked)

Programming

17354 readers
250 users here now

Welcome to the main community in programming.dev! Feel free to post anything relating to programming here!

Cross posting is strongly encouraged in the instance. If you feel your post or another person's post makes sense in another community cross post into it.

Hope you enjoy the instance!

Rules

Rules

  • Follow the programming.dev instance rules
  • Keep content related to programming in some way
  • If you're posting long videos try to add in some form of tldr for those who don't want to watch videos

Wormhole

Follow the wormhole through a path of communities !webdev@programming.dev



founded 1 year ago
MODERATORS
 

I prefer simplicity and using the first example but I'd be happy to hear other options. Here's a few examples:

HTTP/1.1 403 POST /endpoint
{ "message": "Unauthorized access" }
HTTP/1.1 403 POST /endpoint
Unauthorized access (no json)
HTTP/1.1 403 POST /endpoint
{ "error": "Unauthorized access" }
HTTP/1.1 403 POST /endpoint
{
  "code": "UNAUTHORIZED",
  "message": "Unauthorized access",
}
HTTP/1.1 200 (🀑) POST /endpoint
{
  "error": true,
  "message": "Unauthorized access",
}
HTTP/1.1 403 POST /endpoint
{
  "status": 403,
  "code": "UNAUTHORIZED",
  "message": "Unauthorized access",
}

Or your own example.

top 30 comments
sorted by: hot top controversial new old
[–] VonReposti@feddit.dk 6 points 2 months ago

The documented one. It is hell to work with APIs where only the happy path is documented.

[–] gencha@lemm.ee 4 points 2 months ago (3 children)

Respect the Accept header from the client. If they need JSON, send JSON, otherwise don't.

Repeating an HTTP status code in the body is redundant and error prone. Never do it.

Error codes are great. Ensure to prefix yours and keep them unique.

Error messages can be helpful, but often lead developers to just display them in the frontend, breaking i18n. Some people supply error messages in multiple languages, depending on the Accept-Language header.

[–] Michal@programming.dev 2 points 2 months ago

This guy backends ☝️

[–] pe1uca@lemmy.pe1uca.dev 1 points 2 months ago

but often lead developers to just display them in the frontend

Oh boy I feel this one.
My API is meant for scripting (i.e. it's for developers and the errors are for developers), but the UI team uses it and they just straight display the error from their HTTP request for none technical people which might also not get to know all the parameters actually needed for the request.
And even when the error is in fact in my code, and I sent all the data I need to debug and replicate the error, the users can't tell me because the UI truncates the response, so the user only sees something like Error in pe1uca's API: {"error":"bad request","message":"Your request has an error, please check th... (truncated). So the message gets truncated and the link to the documentation is also never shown .-.

[–] FizzyOrange@programming.dev 0 points 2 months ago (1 children)

To be fair if it's an exceptional error message (i.e. database timeout; not incorrect password) I don't think i18n matters that much. Most people will just be googling the error message anyway, and if not it should be rare enough that using Google translate isn't an issue.

[–] azertyfun@sh.itjust.works 3 points 2 months ago

If anything i18n makes things way worse for everyone. Ever tried to diagnose a semi-obscure Windows or Android error on a non-English locale? Pretty sure that's one of the activities in the inner circles of Hell. Bonus points if the error message is obviously machine-translated and therefore semantically meaningless.

Unique error codes fix this if they remain visible to the user, which they usually don't because Mr Project Manager thinks it looks untidy.

[–] xiodine@programming.dev 3 points 2 months ago (2 children)
[–] Metju@lemmy.world 1 points 2 months ago

This is the right answer imo. While it might be an overkill for sth like 404s, it's amazing for describing different bad requests.

[–] iso@lemy.lol 1 points 2 months ago

This one looks nice. Very detailed.

[–] ramble81@lemm.ee 2 points 2 months ago (3 children)

Giving back a 200 for an error always makes me bristle. Return correct codes people. β€œBut the request to the web server was successful!”

[–] OneCardboardBox@lemmy.sdf.org 2 points 2 months ago

I worked on a product that was only allowed to return 200 OK, no matter what.

Apparently some early and wealthy customer was too lazy to check error codes in the response, so we had to return 200 or else their site broke. Then we'd get emails from other customers complaining that our response codes were wrong.

[–] gencha@lemm.ee 1 points 2 months ago

I don't necessarily disagree, but I have spent considerable time on this subject and can see merit in decoupling your own error signaling from the HTTP layer.

No matter how you design your API, if you're passing through additional layers, like load balancers and CDNs, you no longer have full control over all responses your clients receive. At this point it may be viable to always signal a successful backend connection with a 200, even if the process resulted in a failure.

Going further, your API may include partial success scenarios, think batch processing, then the result could be a mix of success and failure that doesn't translate to HTTP status.

You could even argue that there is really no reason to couple your API so tightly with a concept of the transport layer it uses.

[–] FizzyOrange@programming.dev 0 points 2 months ago (1 children)

I use this big expensive simulator called Questa, and if there's an error during the simulation it prints Errors: 1, Warnings: 0 and then exits with EXIT_SUCCESS (0)! I tried to convince them that this is wrong but they're like "but it successfully simulated the error". πŸ€¦πŸ»β€β™‚οΈ

We end up parsing the output which is very dumb but also seems to be industry standard in the silicon industry unfortunately (hardware people are not very good at software engineering).

[–] Dark_Arc@social.packetloss.gg 1 points 2 months ago* (last edited 2 months ago) (1 children)

That's when you use different exit codes. 1 for failure during simulation, 2 for simulation failed.

Shame they wouldn't listen.

[–] fuzzzerd@programming.dev 1 points 2 months ago

I generally agree, but with robocopy they went too far with this, because the status code doesn't work the way you expect, and you've got to script around it.

[–] elrik@lemmy.world 2 points 2 months ago

JSON Problem Details

https://datatracker.ietf.org/doc/html/rfc9457

  • It has a specification, so a consumer of the API can immediately know what to expect.
  • It has a content type, so a client sdk can intelligently handle the response.
  • It supports commonly needed members which are a superset of all of the above JSON examples, including type for code and repeating the http status code in the body if desired.
  • It is extensible if needed.
  • It has been defined since at least 2016.

This specification's aim is to define common error formats for applications that need one so that they aren't required to define their own ...

So why aren't you using problem details?

[–] houseofleft@slrpnk.net 2 points 2 months ago* (last edited 2 months ago)

I'm a data engineer, and have seen an ungodly ammount of 200-but-actually-no-stuff-is-broken errors and it's the bane of my life!

We have generic code to handle pulling in api data, and transforming it. It's obviously check the status code, but any time an API implements this we have to choose between:

  • having code fail wierdly further down the line because can't parse the status
  • adding in some kind of insane if not response.ok or "actually no there's an error really" in response.content logic

Every time you ignore protocols and invent your own, you are making everyone sad.

Will take recommendations of support groups I can join for victims of terrible apis.

[–] killabeezio@lemm.ee 1 points 2 months ago

The status code that gets returned should be the status code of the messenger and not the data. If you want to add a status code about the data, then please do.

If something can return null and empty and it's valid, that is not a 404. That is a 200.

As far as a 403, the messenger is telling you that you shall not pass. There is no data. 403 is appropriate here. The return response can be anything since 403 is pretty self explanatory, but I would probably return json to be consistent. I would also use the field message. Something like the first one for this use case only.

In other cases where i do get data, I would use data, message, status (optional). But status in the json response would be status about the message.

[–] calcopiritus@lemmy.world 1 points 2 months ago

My favourite is when every error is an HTTP Bad Request with no body. Absolutely wonderful to use those APIs

[–] thesmokingman@programming.dev 1 points 2 months ago

There are competing interests here: normal consumers and script kiddies. If I build an API that follows good design, RFCs, pretty specs, all of that, my normal users have a very good time. Since script kiddies brute force off examples from those areas, so do they. If I return 200s for everything without a response body unless authenticated and doing something legit, I can defeat a huge majority of script kiddies (really leaving denial of service). When I worked in video games and healthcare, this was a very good idea to do because an educated API consumer and a sufficiently advanced attacker both have no trouble while the very small amount of gate keeping locks out a ton of annoying traffic. Outside of these high traffic domains, normal design is usually fine unless you catch someone’s attention.

[–] lengau@midwest.social 1 points 2 months ago (1 children)

At a previous job we had an unholy combination of the last two:

HTTP/1.1 200 POST /endpoint 
{
  "data": null,
  "errors": ["403", "unauthorized"],
  "success": false
}

That really is unholy and I also couldn't work there long if they thought that was OK (pun intended).

[–] ShortFuse@lemmy.world 0 points 2 months ago* (last edited 2 months ago) (1 children)

Don't use JSON for the response unless you include the response header to specify it's application/json. You're better off with regular plaintext unless the request header Accept asked for JSON and you respond with the right header.

That also means you can send a response based on what the request asked for.

403 Forbidden (not Unauthorized) is usually enough most of the time. Most of those errors are not meant for consumption by an application because it's rare for 4xx codes to have a contract. They tend to go to a log and output for human readers later, so I'd lean on text as default.

[–] BrianTheeBiscuiteer@lemmy.world 0 points 2 months ago (1 children)

I would actually encourage error responses be in JSON if your 200 responses are JSON. Some clients are apt to always convert the body to JSON so it could avoid an exception on the client side not to throw a curveball.

To your point it's most important that the content and Content-Type header match.

[–] lemmyvore@feddit.nl 1 points 2 months ago

If any client app is blindly converting body to JSON without checking (at the very least) content type and size, they deserve what they get.

If you want to make it part of your API spec to always return JSON that's one thing, but don't do it to make up for poorly written clients. There's no end of ways in which clients can fail. Sticking to a clear spec is the only way to preserve your sanity.

[–] AsudoxDev@programming.dev 0 points 2 months ago* (last edited 2 months ago) (1 children)

Is the last one real? Has any sane dev made something like that monstrosity? It's not like the others are any better, but who would ever think of doing the last one and why?

[–] FourPacketsOfPeanuts@lemmy.world 1 points 2 months ago* (last edited 2 months ago)

I have worked for financial institutions that have variations of the last one. If I saw it I wouldn't even blink. Semi realistic reasons might be:

Status attribute - because the project is using the base library of [project whatever] which was the brain child of eNtErPrIsE aRcHiTeCt whose hands on skills are useless and the off-shore dev team who assigned [random newbie] because that's who was available at the time. They used a status attribute because they didn't know how to get the status of the http response. No-one with budget control is interested in hearing about technical debt at the moment. Everyone has to use it now else the poorly written test classes fail.

Message code: because "we need codes that won't ever change even if the message does". Bonus points if this is, in fact, never used as intended and changes more frequently than...

Message: "because we still need to put something human readable in the log". Bonus points x2 if this is localised to the location of the server rather than the locale of the request. Bonus x3 if this is what subsequent business logic is built on leading to obscure errors when the service is moved from AWS East Virginia to AWS London (requests to London returning "colour" instead of "color" break [pick any service you never thought would get broken by this]).

I have seen it all etc

[–] SorteKanin@feddit.dk 0 points 2 months ago (1 children)

A simple error code is sufficient in all of these cases. The error provided gives no additional information. There is no need for a body for these responses.

[–] kogasa@programming.dev 0 points 2 months ago (1 children)

There may be a need for additional information, there just isn't any in these responses. Using a basic JSON schema like the Problem Details RFC provides a standard way to add that information if necessary. Error codes are also often too general to have an application specific meaning. For example, is a "400 bad request" response caused by a malformed payload, a syntactically valid but semantically invalid payload, or what? Hence you put some data in the response body.

[–] SorteKanin@feddit.dk 1 points 2 months ago

A plain 400 without explanation is definitely not great UX. But for something like 403, not specifying the error may be intentional for security reasons.