go 55 lines · 8 steps

Centralized error handling in Gin

A custom error type plus a tail middleware turns scattered handler errors into consistent JSON responses.

Explained by highlit
1package middleware
2 
3import (
4 "errors"
5 "net/http"
6 
7 "github.com/gin-gonic/gin"
8)
9 
10type APIError struct {
11 Status int `json:"-"`
12 Code string `json:"code"`
13 Message string `json:"message"`
14}
15 
16func (e *APIError) Error() string {
17 return e.Message
18}
19 
20func NewAPIError(status int, code, message string) *APIError {
21 return &APIError{Status: status, Code: code, Message: message}
22}
23 
24var (
25 ErrNotFound = NewAPIError(http.StatusNotFound, "not_found", "resource not found")
26 ErrUnauthorized = NewAPIError(http.StatusUnauthorized, "unauthorized", "authentication required")
27)
28 
29func ErrorHandler() gin.HandlerFunc {
30 return func(c *gin.Context) {
31 c.Next()
32 
33 last := c.Errors.Last()
34 if last == nil {
35 return
36 }
37 
38 var apiErr *APIError
39 if errors.As(last.Err, &apiErr) {
40 c.AbortWithStatusJSON(apiErr.Status, gin.H{"error": apiErr})
41 return
42 }
43 
44 if last.Type == gin.ErrorTypeBind {
45 c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
46 "error": gin.H{"code": "invalid_request", "message": last.Err.Error()},
47 })
48 return
49 }
50 
51 c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
52 "error": gin.H{"code": "internal_error", "message": "something went wrong"},
53 })
54 }
55}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Implementing the error interface lets a struct carry HTTP status and machine-readable codes while still working as a plain error.
  2. 2Running logic after c.Next() lets one middleware inspect everything handlers recorded and shape the final response.
  3. 3errors.As unwraps an error chain so you can branch on a concrete type rather than string-matching messages.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Centralized error handling in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code