Handle errors the right way — Golang

30 Jan 2020 ⏱️ 4 min
Handle errors the right way — Golang

When you start working on a application, there are always some point of failures which can occur, a good programmer has to deal with handling with such unexpected situations in one or the other way.

Recently while working on microservices, I learnt how to handle errors in a way which is easy to understand and covers all edge cases.( well, not all but most 😛)


Simple Hack

This is basically just printing out errors using —

errors.New("some error")

This is not the way you want to go about handling errors all over your service, since there is no structure in these errors. For someone who is new to your codebase, it’s really hard to debug this error.Also, there can be multiple instances where you use similar error text eg. “marshal error”

To solve this, I have seen many people use custom Error structs like —

type Errors struct {
  Code            string `json:"code"`
  Message         string `json:"message"`
  MessageTitle    string `json:"message_title"`
  MessageSeverity string `json:"message_severity"`
}

This is a good way to structure your errors. To explain more on this —

  • Code — eg. “service-name.errors.parse_file_error”. Here you can clearly identify that in which service this error is coming.
  • Message — Message for users if you send it from your service. Helps developer too if he/she has context of service.
  • MessageTitle — This can be optional. It is for showing title on frontend if required. Eg. “Something went wrong”
  • MessageSeverity — How severe this error is — low, medium or high priority.

But wait, now you figured out how to handle error, but there is still a problem on where you handle error and convert it into custom struct.


Make custom struct at the root level(s)

I’ll explain this using an example.
Suppose you are handling errors in some handler which is a part of API where we show user’s post on.

func Handler() http.HandlerFunc {

  return func(w http.ResponseWriter, r *http.Request)
  {
    w.Header().Set("Content-Type", "application/json")
    if userID == "" {
		SendErrorResponse(w, parameterMissing)
	     return
    }
    isUserAuthenicated, err := AuthService.authenticate(userID)
    if err != nil {
       SendErrorResponse(w, "authservice.errors.invalid_user_type")
       return
    }

    userPost, err := PostService.fetchPosts(userID)
    if err != nil {
       SendErrorResponse(w, "PostService.errors.cannot_fetch_post")
       return
    }

    ...
    ...
    ...

    encodedResponse, err2 := json.Marshal(somefinalresponse)
    if err2 != nil {
      SendErrorResponse(w, "XYZ_service.errors.cannot_marshal")
      return
    }
    w.WriteHeader(http.StatusOK)
    w.Write(encodedResponse)
    return
  }
}

SendErrorRespone is just a function which create our custom Error struct which just takes a Code as input.

func SendErrorResponse**(w http.ResponseWriter, errorCode string) {
  response := Errors{
    Code:         errorCode,
    Message:      "some error message",
    MessageTitle: "some error title",
    MessageSeverity: "high"
  }
  body, _ := json.Marshal(response)
  w.WriteHeader(http.StatusBadRequest)
  w.Write(body)
}

This is the root level of this API so here is when you need to classify your errors into a structured way.(You can also have 1 or 2 sublevels too also classify but it’s subjective). The handler can call 10’s of other functions, you just need to identify in which of these functions you are getting the error.


But, if we make custom error at root level then how to handle errors in sublevels?

Error handling by grouping into custom errors{: .align-center}

Error handling by grouping errors into custom errors

You can simply return golang error (say errors.New("xyz error")) at your sublevel, return them and group them finally to a custom Error at root level. You will have to take a call on which level you want to group your errors into a single custom Error.


You can question why not keep custom error everywhere?

Well, the answer to this is YES, you can. When working on large applications, I prefer to group errors within a single functionality to a custom.

Finally, It’s upto you in which way you want to implement. I believe now you’ll implement error handling in better way than your past projects. Also, refactoring your current codebase would be a good way to learn this way of error handling.

Relevant link - Don’t just check errors, handle them gracefully

Liked the article? Consider supporting me ☕️


I hope you learned something new. Feel free to suggest improvements ✔️

I share regular updates and resources on Twitter. Let’s connect!

Keep exploring 🔎 Keep learning 🚀

Liked the content? Do support :)

Paypal - Mohit Khare
Buy me a coffee