0

Let's focus on a single endpoint, which always returns an encoded JSON object with a well defined and documented structure.

But when an exception is thrown, the exception itself is formatted to a JSON object with the next structure:

{
 code: 462
 message: 'whatever specific error occurred'
}

Is this legit or all responses for a resource should share the same structure?

I mean, for every possible response something like:

{
   code: 200
   message: success
   data: {...}
}

where data is filled or empty depending if it was a success or an exception.

  • Do you like the "one data model to rule them all and in the darkness bind'em" feeling when developing code on the server-side? If yes, then it's legit, if not (and I hope you don't) why should it be different on the client-side? In other words. Make these questions as if you were the consumer of the API. – Laiv Jun 6 at 12:09
  • I dunno, to tell you the truth, i had different responses for the exceptions but then an Android dev complained that he needed to do some manual checks because of that... Hence my doubts – vivoconunxino Jun 6 at 12:18
  • 1
    Exactly. That's the point. Some clients are coded with strongly typed programming languages. In such a case, the advantages we use to have in web developments doesn't exist. Your goal is to make the API as consumable as possible for any sort of clients. If your only consumer is your own Android client, ask your co-workers which model is easier for them to consume. – Laiv Jun 6 at 12:29
3

The format of the response body for an error like 404 can be defined in the Content-Type in the header of the response (Content-Types can be more than just "json", you can define and document your own Content-Types that explain the actual format of the error. See here. Clients can then know what data they are getting back when something happens)

This doesn't have to be the same Content-Type as returned for a 200. It is up to you.

From the HTTP spec

Client Error 4xx

... Except when responding to a HEAD request, the server SHOULD send a representation containing an explanation of the error situation, and whether it is a temporary or permanent condition.

Seems rather redundant to include the HTTP status code in the body of the response since it is in the HTTP response itself, but nothing says you can't.

  • 1
    Thanks, I agree that it seems redundant – vivoconunxino Jun 6 at 13:37
1

I am a regular user of the Stack Exchange API. It uses the same schema for errors and for successful responses, the wrapper. Of course, different fields will be filled; just compare the responses to the Site Information call for an existing site: https://api.stackexchange.com/2.2/info?site=softwareengineering and a non-existing one: https://api.stackexchange.com/2.2/info?site=dummy.

Yet, if you look at the (simplified) boilerplate Java code I use to call the API, you see that it wouldn't make a difference if the response would have a different structure:

// Execute request
connection.connect();
try {
    try (InputStream inputStream = connection.getInputStream();
            GZIPInputStream gzipStream = new GZIPInputStream(inputStream)) {
        JSONObject jRoot = new JSONObject(IOUtils.toString(gzipStream, "UTF-8"));

        // Backoff?
        if (jRoot.has("backoff")) {
            int backoff = jRoot.getInt("backoff");
            System.out.println("Backoff: " + backoff);
            Thread.sleep(1000 * backoff);
        }

        return jRoot;
    }
} catch (Exception e) {
    try (InputStream inputStream = connection.getErrorStream();
            GZIPInputStream gzipStream = new GZIPInputStream(inputStream)) {
        JSONObject jRoot = new JSONObject(IOUtils.toString(gzipStream, "UTF-8"));
        if (throwAPIException) {
            throw new APIException(jRoot.getString("error_message"));
        }
        return null;
    }
}

The catch here is that an error response by the API returns a 4xx or 5xx HTTP status code, which will raise an exception in connection.getInputStream() and necessitates my code to read the response from the error stream instead. So even if I would deserialize it into typed objects (e.g. with Gson), it's fine to have a different structure for success and failure.

  • Yep, that's exactly what I do when using my own API o a frontend – vivoconunxino Jun 6 at 12:30
  • 2
    This is a nice example of a tolerant reader. I'm a fan of this sort of message handling but I have to say that this answer is not addressing the design concern. It's just explaining how clients can handle it. Ways of handling problems like this are many but none addresses the main problem "should we represent two different messages with a single message data model"? The answer, as usual, is "depends". – Laiv Jun 6 at 12:37
1

I had different responses for the exceptions but then an Android dev complained that he needed to do some manual checks because of that.

This is more important than any of the answer here.

If you know the audience to whom the API is addressed you should hear them first. If they have different opinions, sit with them and allow them to expose their concerns. It's likely the resulting model will be the one that takes less time to implement for everybody and yet covers the needs for data transfer. If I'm allowed to opinion, I'm sure that teams programming with strong-typed languages will ask for different models. To my experience, to design web APIs thinking in the most constrained or limited client has led me to more consistent and robust APIs. That said, I have to confes that I use to design APIs for mobile apps. However, when I have had to support web applications (SPAs mostly) I have never had problems at all.

It's different when we don't know the audience which could be the case of Stack Exchange API. When we make an API public we are forced to make decisions thinking in the best scenario possible for everybody. Since it's impossible to make happy everybody, whatever we choose is going to be ok as soon as it doesn't make our job harder unnecessarily.

One way or another, it's important to be consistent with the decision. Don't mix up both solutions here and there.

Finally, as @CormacMulhall says, use the HTTP mechanisms to communicate intention, data and errors. Try not ignoring the HTTP semantics either or you will find no value in building Web APIs.

  • Thanks, I'll try to get to an agreement with them – vivoconunxino Jun 6 at 15:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.