

Best Practices for API Error Handling in Go
source link: https://www.vultr.com/docs/best-practices-for-api-error-handling-in-go
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Introduction
This guide explains error handling in Go and the best practices for handling API errors in Go. You should have a working knowledge of Go and understand how web APIs work to get the most benefit from this article.
Error Handling in Go
Go has a unique approach to handling errors. Rather than utilizing a try..catch block like other programming languages such as C++, Go treats errors as first-class values with panic and recovery mechanisms.
Go has a built-in type called error, which exposes an interface that implements the Error() method:
type error interface {
Error() string
}
Typically the usual way to handle errors in Go is to check if the returned error value is nil. If it's equal to nil, then it means no errors occurred.
Go functions can also return multiple values. In cases where a function can fail, it's a good idea to return the error status as a second return value. If the function doesn't return anything, you should return the error status.
func myFunc() error {
// do something
}
err := myFunc()
if err != nil {
// do something
}
You can define errors using the errors.New() function, and have it printed out to the console using the Error() method:
func myFunc() error {
myErr := errors.New(My Error”)
return myErr
}
err := myFunc()
if err != nil {
fmt.Println(err.Error())
}
This prints our defined error(My Error") to the screen.
The panic and recovery mechanisms work a bit differently. As panic halts program flow and causes the execution to exit with a non-zero status code, and then prints out the stack trace and error.
if err != nil {
panic(err)
}
When a function calls panic(), it won't be executed further, but all deferred functions will be called. Recover is typically used with this defer mechanism to rescue the program from the panic.
func panicAndRecover() {
defer func() {
if err := recover(); err != nil {
fmt.Println(Successfully recovered from panic”)
}
}()
panic(Panicking")
}
When the panicAndRecover function is called, it panics, but rather than returning a non-zero exit status and exiting, it runs the deferred anonymous function first. This recovers from the panic using recover(), and prints out to the screen. Normal program execution occurs without exiting immediately as we successfully recover from panic.
API Error Handling
When building web APIs, appropriately handling errors is an integral part of the development process. Examples of such errors include JSON parsing errors, wrong endpoint requests, etc.
Let's dive into some of the best practices for handling API errors in Go:
Using Appropriate HTTP Status Codes
HTTP status codes communicate the status of an HTTP request; you must carefully return status codes representing the state of a request.
HTTP status codes are split into five categories:
- 1xx - Information
- 2xx - Success
- 3xx - Redirection
- 4xx - Client error
- 5xx - Server error
Many developers using your API might rely solely on the status code to see if the request was successful. Sending a 200 (success) code followed by an error is bad practice. Instead, it is proper to return a more appropriate code, such as 400 (bad request).
For instance, consider the following reqChecker function, which takes a request body and returns nil or some error value, depending on the condition of the request body. An http.StatusOK
(200 status code) is misleading if the request body doesn't conform to our standard.
func checker(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
if err := reqChecker(r.Body); err != nil {
fmt.Fprintf(w, Invalid request body error: %s”, err.Error())
}
}
We can refactor the handler to return a more appropriate error code:
func checker(w http.ResponseWriter, r *http.Request) {
if err := reqChecker(r.Body); err != nil {
w.WriteHeader(http.StatusBadRequest) // 400 http status code
fmt.Fprintf(w, Invalid request body error:%s”, err.Error())
} else {
w.writeHeader(http.StatusOK)
}
}
There are quite a large number of HTTP status codes available, but just a subset of them are actually used in practice, with the most common ones being:
- 200 - Ok
- 201 - Created
- 304 - Not modified
- 400 - Bad Request
- 401 - Unauthorized
- 403 - Forbidden
- 404 - Not Found
- 500 - Internal Server Error
- 503 - Service unavailable
It's not ideal to use too many status codes in your application. Keeping it to a minimum is recommended practice.
Descriptive Error Messages
While sending proper HTTP status error codes is a very important step in handling API errors, returning descriptive messages provides the client with additional information.
Multiple errors could return the same error code. For instance, posting a wrong JSON request format and malformed requests could spawn the same http.StatusBadRequest
error (status code 400). The status code indicates that the request failed due to the client's error but didn't provide much about the nature of the error.
Returning a JSON response to the client alongside the error code like the following is more descriptive:
{
"error": "Error parsing JSON request",
"message": "Invalid JSON format",
"detail": "Post the 'author' and 'id' fields in the request body."
}
Note: Descriptive error messages should be framed carefully not to expose the inner workings of the API to attackers.
The error field within the response should be unique across your application, and the detail field should provide more information about the error or how to fix it.
Avoid generic error messages, as it's much better to know why specifically the request failed rather than a generic error code.
Exhaustive Documentation
Documentation is an essential part of API development. While API errors can be handled with ease by formulating proper responses when errors occur, comprehensive documentation helps the clients know all the relevant information concerning your API, such as what endpoints are provided by your API, the response and request formats, parameter options, and more.
The more exhaustive your documentation, the less likely clients will spend time battling API errors.
Conclusion
In this guide, we have learned about error handling in Go and some of the best practices for handling API errors.
Want to contribute?
You could earn up to $600 by adding new articles
Recommend
-
11
Error handling best practice yourbasic.org/golang Go has two different error-handling mechanisms: most functions return
-
16
React Error Handling and Logging Best PracticesHow to handle errors and properly log them using the right tools
-
9
5 Best Practices for Handling State Structure in ReactFive best practices that can help to structure the state wellWhen we write a component in React that holds some state, we will have to make choices...
-
7
Blog Post Learn best practices for debugging and error handling in an enterprise-grade blockchain application Identify where failures take place and resolve them ...
-
9
Blazor Best Practices: Handling Errors
-
12
Quick Microsoft .NET 6 Overview Microsoft .NET 6 is a cross-platform framework that merges the .NET Core, .NET Framew...
-
4
Introduction One of the challenges of working with any graphics API is that under the hood they tend to be very asynchronous in nature. The work you send to the GPU isn’t completed the moment you call draw or dispatch,...
-
9
5 Best Practices in Handling HTTP Errors in JavaScript Errors are an everyday obstacle that all developers and clients face. A typical error is when applications or a particular fun...
-
11
Avoiding Silent Failures in Python: Best Practices for Error Handling By Bob Belderbos on 7 August 2023 ...
-
6
[Link] API error handling, why should you care?Happy Goat for Life! [Link] API error handling, why should you care?
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK