RESTful API is a common tool of building web services, especially in front and back-end separated application. It is based on HTTP protocol, which is simple, text-oriented, and well supported by various languages, browsers or clients. However, REST is not yet standardized, so that the developers need to decide how to design their APIs. One of the decisions is error handling. Should I use HTTP status code? How to handle form validation errors, etc. This article will propose an error handling mechanism for RESTful API, based on my daily work and understanding of this technique.
Types of Errors
I tend to categorize errors into two types, global and local. Global errors include requesting an unknown API url, not being authorized to access this API, or there’s something wrong with the server code, unexpected and fatal. These errors should be caught by the web framework, no customized handling in individual API function.
Local errors, on the other hand, are closely related to the current API. Examples are form validation, violation of unique constraint, or other expected errors. We need to write specific codes to catch these errors, and raise a global error with message and payload for framework to catch and respond with.
Flask, for instance, provides a mechanism to catch exceptions globally:
1 | class BadRequest(Exception): |
Error Response Payload
When you post to /person
with an empty username
, it’ll return the following error response:
1 | HTTP/1.1 400 Bad Request |
There’re several parts in this response: HTTP status code, a custom status code, error message, and some extra information.
Use HTTP Status Code
HTTP status code itself provides rich semantics for errors. Generally 4xx
for client-side error and 5xx
server-side. Here’s a brief list of commonly used codes:
200
Response is OK.400
Bad request, e.g. user posts some in valid data.401
Unauthorized. WithFlask-Login
, you can decorate a route with@login_required
, and if the user hasn’t logged in,401
will be returned, and client-side can redirect to login page.403
Access is forbidden.404
Resource not found.500
Internal server error. Usually for unexpected and irrecoverable exceptions on the server-side.
Custom Error Code
When client receives an error, we can either open a global modal dialog to show the message, or handle the errors locally, such as displaying error messages below each form control. For this to work, we need to give these local errors a special coding convention, say 400
for global error, while 40001
and 40002
will trigger different error handlers.
1 | fetch().then(response => { |
More Error Information
Sometimes it is ideal to return all validation errors in one response, and we can use payload
to achieve that.
1 | { |
Fetch API
For AJAX request, Fetch API becomes the standard library. We can wrap it into a function that does proper error handling. Full code can be found in GitHub (link).
1 | function request(url, args, form) { |
This method will reject the promise whenever an error happens. Invokers can catch the error and check its status
. Here’s an example of combining this approach with MobX and ReactJS:
1 | // MobX Store |