In REST APIs, http error codes 4XX, 5XX are used to convey error messages on the server.
When the API itself is dependent on another backend API, is it a good practice to expose error codes or error information from the backend API call? Is there a standard for it?
Can we share the the following in the error response with 5XX?
- Url of the backend url
- parameters sent
- error response from backend api (when reachable)
Motivation
- Easy for consumer to understand error in the backend api
- No need to create mapping of error codes between frontend api and backend api
However, this is not desirable outside the Business Unit or the organization.
2
Remember that a REST API is an abstraction. The fact the REST API depends on other APIs is an implementation detail. Clients should be coupled to your REST API, not the other systems the REST API depends on. As soon as you pass error codes and URLs to the client, they can become coupled to your REST API’s dependencies during a critical part of error handling. This would not be good practice, because error handling can be tricky. You don’t want downstream changes to affect your clients, and that is precisely what happens when client error handling relies on any downstream information from the REST API.
Instead, your REST API should return a use case-specific error, and hide all details about the downstream dependency. Doing this:
- Avoids temporal coupling between your clients and your downstream dependencies.
- Hides sensitive information that an attacker might use to exploit your system or your downstream dependencies.
Yes, it means coming up with your own error mapping, but don’t get any more detailed than is necessary for the client to recover from that error in that particular use case. If no specific error mapping is useful, return a generic 500 Server Error
message or JSON response.
When the api itself is dependent on another backend API, it is a good practice to expose error codes or error information from the backend api call?
This is the wrong question (for “good practice”) since the problem you are presenting is very dependent on the context. So I am offering a general reasoning here.
The leading questions are:
-
Who is your audience?
-
What is the utility of the message you are providing?
Enduser-Scenario
Say you are developing an API and your audience is the end user and his browser. What difference makes it to the user knowing “What I just did wasn’t successful” or “What I just did wasn’t successful, but its somebody elses fault deep down the rabbit hole”. One scanrio where this may be interesting is payments
. The information the payment provider has a problem could help a user to decide retrying what he did with another payment provider. But there may be many other scenarios where the additional informations is of zero utility.
Developer(Debug)-Scenario
Say your audience are developers consuming your API. The result is the same as in the enduser scenario but with the difference that they could use this information to put it in the right context: “Oh, it’s not my fault querying the API wrongly nor is it a bug in the API it’s further down the river. So I am fine” . The given extra information is useful in this scenario but for my taste deliverd via the wrong medium. Which leads us to the last scenario.
Production-Scenario
For production I would expect the information “The downstream API has a hiccup” appears somewhere in the logs
(whatever you use). So in case anybody interested could retrieve the information (for debugging) from there. Most often you want a retry
-machanism for the downstream request and use a circuit breaker which helps minimizing hiccups bubbling further upstream. And when there are endusers involved you may or may not want to provide further information.
I hope this helps deciding what you want to implement.
1
This is my very strong opinion, but a 500 error tells me you are not performing error handling correctly. 500 series errors are supposed to be for infrastructure issues and the default error handler for most web frameworks send that back when errors bubble out of your application.
- 400 Bad Request means that the information presented did not make sense to the server
- 404 There is no resource found
When it comes to errors where the request is well formed but there is some other logical error, there isn’t a well defined or agreed upon standard. That said, there are a few options:
- 403 Forbidden
- 409 Conflict
- 422 Unprocessable Entity
That said, 403 is probably the most appropriate. Essentially the request is well formed, but the application is refusing to process the request.
Do return a body with the error. If the endpoint typically returns JSON, XML, YAML, or some other structured format the error should be formatted in the same manner. Otherwise, you can return simple text or even an HTML page.
6
If the client application is a web-based one, HTML may fit perfectly.
Just attach a text/html
content to your error response from any REST-based endpoint and you’re done. Works with i18n, parameters, specific scenarios, very precise error messages. All without the need for you to constantly map between “services”.
Works even better, if your services link to each other instead of directly calling.
Yes,
Lets assume that the client library you are using to call the downstream api throws and exception with the error message. Unless you specifically want to hide or handle that exception the standard thing to do would be to bubble it up the stack and return it to the caller.
In this case thats done by returning a HTTP response indicating the error with a code (insert RESTful argument about which here) and (most of the time) a message.
However! If the error message the downstream api throws is potentially confusing, you might want to handle it by wrapping in your own exception. ie “Downstream API failed with error: {innerError}” This clearly indicates that the other api needs fixing not yours.
Maybe your language of choice has InnerException or similar, although that raises the question of how to format the HTTP error message to indicate nested exceptions.
Regarding hiding the error message for “outside” use. There are situations where you MUST do this, but I think applying that rule generally is not best practice.
It might be slightly embarrassing to expose that you are using some other company’s api under the hood, but clear error messages that explicitly identify the issue are critical to resolving issues quickly.
"Cant connect to downstream api on www.wrongurl.com"
is better than
"Error 5367912, please contact support"