How should we return non 200 responses?

When creating an API, returning non 200 ok responses can be quite tricky as you might not know what information should be provided to the consumers. A 200 OK response is very straightforward as we understand that it represents a successful request and in the case of a GET, a response can be provided. In the case of a non 200 response however, things can be a bit more tricky. For example, while we know that a 400 response code will indicate that there’s an issue with the request, the consumer might want more information than just a blank response with a 400 http status code.

Some APIs might provide some details in the response to give more insights to the consumers as to what might have just happened.

{
    "error":"First name is required"
}

Other APIs might return a different format, with the intention of conveying mroe information to the consumers:

{
    "message": "Missing parameter",
    "parameter": "FirstName"
}

With the above two examples, the error response is intended to provide more information to the consumer but there is no consistent structure between the two responses. Until now! Welcome to the past or at least 2016..

Problem detail response

In 2016, the IETF (Internet Engineering Task Force) created RFC7807 which is a document to define what was specified as a problem detail response. The idea between a problem detail response is to provide a structured and consistent response that would provide more information in the response. Having a problem detail response would alleviate the need to create new HTTP status codes to represent every single error that could occur on an API request.

So what does it look like?

A problem detail response can be provided using either XML or JSON. In this post, we’ll be looking at a sample JSON problem detail response that can be provided to the consumers to give more information. A problem details can have the following properties:

  • Type - The type should be a URI reference that identifies the problem type. When a user goes to the URI, they would be able to read some documentation of the problem. An exmaple value for this is www.example.com/missing-parameter-error. The type should always be available in any problem detail response.
  • Title - A short human readable summary of the problem type. An exmaple of this would be Missing required parameter
  • Status - A number used to represent the HTTP Status code for the problem detail
  • Detail - A readable explanation specific to the occurance of the problem. So this property should be used to provide more information to the consumer as to what happened. Please enter a first name would be a good detail response
  • Instance - a URI reference that identifies the specific occurrence of the problem.

Let’s use it

Now the properties and specifications are out of the way, what’s an example of a problem detail response? Well, let’s say we are returning a 400 Bad Request response from the API, the following can be sent as a response:

{
    "type":"https://samueltambunan.com/required-first-name",
    "title": "A required parameter has not been inputted",
    "detail": "Please enter the first name as it is a required parameter",
    "status": 400
}

The above can be returned as a response with a 400 Bad Request status code attached to it.

Content types

When returning a problem details response, it’s good to set the content type to application/problem+json. If an XML version of the problem details type is returned, use application/problem+xml.

Extensions property

In the RFC, an Extensions member can be added to the problem details to provide additional information. Clients are recommended to ignore any additional members in the property that they do not need. In C#, the problem details class has an Extensions dictionary property that can be used. When a value is added to the dictionary, it will then be serialized into a top level property.

var problemDetails = new ProblemDetails() {
        
};
problemDetails.Extensions.Add("Balance", 500);

When deserialized, the .NET framework will then serialize all the key and values in the Extensions dictionary into a top level JSON property.

{
    "type":"https://samueltambunan.com/balance-not-enough",
    "title": "Insufficient balance",
    "detail": "The balance in your account needs to be greater than 1000",
    "status": 400,
    "balance": 500
}

Final thoughts

Having a standardised response to a problem seems very useful to me. It would make it easier to consume APIs as I know what responses to expect when an error occurs. In addition, I can focus on using the standard HTTP Status codes when an error occurs and not have to worry about creating unique HTTP Status codes for the API.

Reference

RFC 7807

Updates

2021-03-08: Added an example with extensions as well as content types