Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

REST API error codes return structure

Tags:

json

rest

http

api

I am writing a REST API and I have stumbled upon a problem. What is the best way to return the validation errors.

Until now I have been returning the error messages dumped into a general error code (let's say bad request for example)

{
    "status": 400,
    "error": {
        "code": 1, // General bad request code
        "message": [
                "The Key \"a\" is missing",
                "The Key \"b\" is missing",
                "The Key \"c\" is missing",
                "Incorrect Format for field \"y\""
         ]
    }

)

I have researched a little more about how should a good API response should look like and I thought of the following options:

  1. Stop at the first encountered error and return a response with the specific error code

    {
       "status": 400, //Same as the HTTP header returned
       "error" {
            "code": 1, // Specific field validation error code
            "message": "Field \"x\" is missing from the array structure",
            "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
            "more_info" => "www.api.com/help/errors/1"
        }
    )
    
  2. Parse all the request data and return multiple field validation errors.

    {
      "status": 400,
      "error": {
        "code": 1 //General bad Request code
        "message": "Bad Request",
        "developer_message": "Field validation errors."
        "more_info": "www.api.com/help/errors/1",
        "error_details": {
                0: {
                        "code": 2 // Specific field validation error code
                        "message": "Field \"x\" is missing from the array structure",
                        "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                        "more_info": "www.api.com/help/errors/2"
                    },
    
                1: {
                        "code": 3 // Specific field validation error code
                        "message": "Incorrect Format for field \"y\"",
                        "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                        "more_info": "www.api.com/help/errors/3"
                   }
                       }
          }
      }
    

In my opinion option 2 would be the right way (it gives more useful information to the developers/end users and the server load might be lower(less requests/no need to revalidate valid data/no need to compute the signature and authenticate the user)), but I am wandering what are the best practices, and if there is another way to treat this kind of problems.

Also i think option 1 is still valid if i get a single fatal error in the flow of the script.(not validation errors)

Please note that the code is just a simple array just so it is easier to follow. The response format will be JSON or XML.

like image 459
Sekundes Avatar asked Aug 27 '13 15:08

Sekundes


People also ask

How do you write an error response in REST API?

Error responses MUST use standard HTTP status codes in the 400 or 500 range to detail the general category of error. Error responses will be of the Content-Type application/problem, appending a serialization format of either json or xml: application/problem+json, application/problem+xml.

Should an API ever return 500?

4. Never return 500 errors intentionally. The only type of errors you should be showing to the user intentionally is validation (400) errors. 500 codes are all about something you don't anticipate to happen.

What are the different error status that you would return in restful web services?

Here are some common response codes: 400 Bad Request – client sent an invalid request, such as lacking required request body or parameter. 401 Unauthorized – client failed to authenticate with the server. 403 Forbidden – client authenticated but does not have permission to access the requested resource.


3 Answers

Let's look to Facebook's Graph API. That is hit hard, and a great many errors are most likely generated. Here is what Facebook returns on an API error:

 {
   "error": {
     "message": "Message describing the error", 
     "type": "OAuthException", 
     "code": 190,
     "error_subcode": 460,
     "error_user_title": "A title",
     "error_user_msg": "A message"
   }
 }

They try to make the Graph API as useful as possible, but they seem return a specific error with a code and a subcode (Ref). The fact that each error has its own code means it's easier to search for said code or message as a starting point for debugging. That's probably why they don't accumulate error messages in their official error response. If it's good enough and convenient for Facebook, it's probably good enough for us.

Sample error responses:

{
  "error": {
    "message": "(#200) Must have a valid access_token to access this endpoint", 
    "type": "OAuthException", 
    "code": 200
  }
}

and

"error": {
  "message": "(#604) Your statement is not indexable. The WHERE clause must contain 
   an indexable column. Such columns are marked with * in the tables linked from
   http://developers.facebook.com/docs/reference/fql ", 
  "type": "OAuthException", 
  "code": 604
}

Then there is JSend which "is a specification that lays down some rules for how JSON responses from web servers should be formatted." Their goal is:

There are lots of web services out there providing JSON data, and each has its own way of formatting responses. Also, developers writing for JavaScript front-ends continually re-invent the wheel on communicating data from their servers. While there are many common patterns for structuring this data, there is no consistency in things like naming or types of responses. Also, this helps promote happiness and unity between backend developers and frontend designers, as everyone can come to expect a common approach to interacting with one another.

Here is a sample error message:

{
    "status" : "fail",
    "data" : { "title" : "A title is required" }
}

It looks like Facebook and this group trying to set like an industry standard are opting for your choice #1.


Bounty Question

In response to the bounty request of "if anyone went #2 and maybe has any improvements on it?", there is a design pattern from Pragmatic RESTful API that states:

Validation errors will need a field breakdown. This is best modeled by using a fixed top-level error code for validation failures and providing the detailed errors in an additional errors field, like so:

{
  "code" : 1024,
  "message" : "Validation Failed",
  "errors" : [
    {
      "code" : 5432,
      "field" : "first_name",
      "message" : "First name cannot have fancy characters"
    },
    {
       "code" : 5622,
       "field" : "password",
       "message" : "Password cannot be blank"
    }
  ]
}
like image 186
Drakes Avatar answered Oct 22 '22 18:10

Drakes


I have used #2 myself a couple times. Is it better than #1? I think that depends on what your API is being used for.

I like #2 because it gives a developer who is testing the API with some test calls a quick overview of all the errors/mistakes he made in a request, so he knows immediately which errors/mistakes he has to fix to make that request valid. If you return the errors one by one (like in #1) you have to keep retrying the request and cross fingers hoping it will be valid this time.

But as I said #2 is very useful for developers, but the reasons don't really apply to end users. End users don't usually care how it's implemented. Whether the software is doing 1 request which returns 5 errors or 5 subsequent requests which return 1 error each.
As long as it's handled well in the client, the end user shouldn't notice the difference. How to handle that of course very much depends on what the client actually is.

Next to speeding up the development, another benefit of #2 (in production) is that it requires less requests to be made, which of course decreases the server load.


I would like to know if anyone went #2 and maybe have any improvements on it so I opened a bounty.

Sure there are improvements to be made. As it is, there is some data in the body that can be omitted.

{
  "status": 400,
  "error": {
    "code": 1 //General bad Request code
    "message": "Bad Request",
    "developer_message": "Field validation errors."
    "more_info": "www.api.com/help/errors/1",
    "error_details": {
            0: {
                    "code": 2 // Specific field validation error code
                    "message": "Field \"x\" is missing from the array structure",
                    "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                    "more_info": "www.api.com/help/errors/2"
                },

            1: {
                (
                    "code": 3 // Specific field validation error code
                    "message": "Incorrect Format for field \"y\"",
                    "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                    "more_info": "www.api.com/help/errors/3"
                )
            }
)

With HTTP responses, the status code should not go in the body, but in the header. This means that "status": 400 and "message": "Bad Request" can be omitted here. 400 should be the response's status code, and 400 means Bad Request. It's a HTTP standard and doesn't have to be explained in the response. Also "developer_message": "Field validation errors." is kind of a duplicate, since the specific errors are already included in each seperate error, so we could leave that out.

That leaves

{
  "error": {
    "code": 1 //General bad Request code
    "more_info": "www.api.com/help/errors/1",
    "error_details": {
            0: {
                    "code": 2 // Specific field validation error code
                    "message": "Field \"x\" is missing from the array structure",
                    "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                    "more_info": "www.api.com/help/errors/2"
                },

            1: {
                (
                    "code": 3 // Specific field validation error code
                    "message": "Incorrect Format for field \"y\"",
                    "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                    "more_info": "www.api.com/help/errors/3"
                )
            }
)

"code": 1 //General bad Request code
"more_info": "www.api.com/help/errors/1",

These 2 lines don't really make sense anymore now. They are also not required, since each error has it's own code and info link, so we might strip these lines as well, leaving this

{
  "error": {
    "error_details": {
            0: {
                    "code": 2 // Specific field validation error code
                    "message": "Field \"x\" is missing from the array structure",
                    "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                    "more_info": "www.api.com/help/errors/2"
                },

            1: {
                (
                    "code": 3 // Specific field validation error code
                    "message": "Incorrect Format for field \"y\"",
                    "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                    "more_info": "www.api.com/help/errors/3"
                )
            }
)

The 400 status code already indicates there was an error, so you don't have to indicate "error": {error details} anymore, because we already know there was an error. The list of errors can simply become the root object:

[
    {
        "code": 2//Specificfieldvalidationerrorcode
        "message": "Field \"x\" is missing from the array structure",
        "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
        "more_info": "www.api.com/help/errors/2"
    },
    {
        "code": 3//Specificfieldvalidationerrorcode
        "message": "Incorrect Format for field \"y\"",
        "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
        "more_info": "www.api.com/help/errors/3"
    }
]

So all that's left in the body now is simply a list of errors.

The status code is specified in the response header.
The details are specified in the response body.

like image 38
Tim Avatar answered Oct 22 '22 20:10

Tim


I recently worked against a Rest API that would return multiple warnings or errors in the results. Starting from your Sample #2, I would modify it as follows:

{
  "status": 400,
  "results" : null,
  "warnings": {
        0: {
                // Build a warning message here, sample text to show concept
                "code": 1 // Specific field validation error code
                "message": "It is no longer neccessary to put .js on the URL"
           }
  }
  "errors": {
        0: {
                "code": 2 // Specific field validation error code
                "message": "Field \"x\" is missing from the array structure"
                "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
            },
        1: {
                "code": 3 // Specific field validation error code
                "message": "Incorrect Format for field \"y\"",
                "developer_message": "The field \"y\" must be in the form of \"Y-m-d\""
           }
      }
  }

This would provide you the ability to provide results, with multiple warnings or errors as needed in your response.

And yes, this does have some bloat in the structure, but it also provides an easy interface for a developer to always get their data back in the same structure.

I would also remove the following items as IMHO they should be in the API docs (how to find help using an error code) instead of on every error:

"more_info": "www.api.com/help/errors/2"
"more_info": "www.api.com/help/errors/3"

Along those same lines, I'm not sure if you need both the message and developer_message. They appear to be redundant and as if you are trying to provide user error messages from the API when the caller failed to provide data correctly.

like image 2
Martin Noreke Avatar answered Oct 22 '22 18:10

Martin Noreke