How to Implement Custom Error Handlers with Error Types in Gorig
Gorig provides a structured error-handling framework based on a typed Error struct in utils/errors that maps business logic failures to HTTP responses via the apix package, enabling consistent JSON formatting, automatic logging, and panic recovery.
The jom-io/gorig repository offers a comprehensive solution to implement custom error handlers with error types that standardize how Go applications communicate failures to API consumers. By leveraging the Error struct defined in utils/errors/err.go alongside response utilities in apix/handle.go, developers can ensure that system crashes, validation failures, and coding errors each trigger appropriate HTTP status codes and uniform JSON payloads.
Understanding Gorig's Typed Error Architecture
Gorig categorizes every error into one of three distinct types: Coding, System, and Application. This classification determines how the framework logs the incident and which HTTP status code it returns to the client.
The central Error struct in utils/errors/err.go carries a Type field (string), a Code field (string for numeric identifiers), and a Message. The CodeInt() method parses the string code into an integer, allowing the API layer to override default HTTP statuses when specific business error codes are required.
Core Source Files and Responsibilities
utils/errors/err.go: Defines theErrorstruct, type constants, and constructors (Sys,Verify,Assert,VerifyCode). This is where you create typed error instances.apix/handle.go: ContainsHandle,HandleData,HandleError, andPanicNotify. These functions translate*errors.Errorinto HTTP responses and manage logging.apix/response/response.go: Provides low-level JSON rendering helpers (Success,Fail,ErrorSystem) that standardize the response shape.httpx/mid.recovery.go: Implements theRecovery()Gin middleware that intercepts panics and forwards them toapix.PanicNotifyas typed errors.
Defining Custom Error Types
Create reusable error definitions using the constructor functions from utils/errors. Each constructor returns *errors.Error pre-configured with the appropriate type classification.
import "github.com/jom-io/gorig/utils/errors"
// Application-level error for business rule violations (maps to 4xx responses)
var ErrInsufficientBalance = errors.Verify("balance not enough for the requested operation")
// System-level error for infrastructure failures (maps to 500 responses)
var ErrDatabaseConnection = errors.Sys("cannot connect to database")
// Coding-level error for invalid arguments or assertions
var ErrInvalidUserID = errors.Assert("userID", "value cannot be empty")
// Custom numeric code for specific HTTP status mapping
var ErrResourceNotFound = errors.VerifyCode(404, "resource not found")
Key constructors:
Sys(message, nativeErr...): CreatesSystemtype errors for database, network, or file system failures.Verify(message, nativeErr...): CreatesApplicationtype errors for validation or business logic violations.VerifyCode(code, message, nativeErr...): CreatesApplicationerrors with an explicit numeric code that overrides the default HTTP status.Assert(key, value): CreatesCodingtype errors for invalid arguments or programming assertions.
Configuring Global Error Recovery
Before handling specific errors in controllers, register the Recovery middleware to capture unhandled panics and convert them into typed errors automatically.
import "github.com/jom-io/gorig/httpx"
func main() {
// Startup creates the Gin engine and registers Recovery middleware
httpx.Startup("myService", "8080")
}
The Recovery() middleware defined in httpx/mid.recovery.go wraps every HTTP handler. When a panic occurs, it recovers, constructs a System type *errors.Error, and invokes apix.PanicNotify, which logs the stack trace and optionally sends alerts before returning a sanitized 500 response to the client.
Implementing Error Handlers in Controllers
The apix package provides three primary functions to implement custom error handlers in your Gin controllers. Each accepts an *errors.Error and produces a consistent JSON response with the shape {code: <int>, msg: <string>, data: <any>}.
Handling Native Errors with HandleError
Use HandleError when you need to wrap a standard Go error and control the HTTP status code explicitly.
func GetUser(c *gin.Context) {
user, err := svc.FindUserByID(c.Param("id"))
if err != nil {
// Convert native error to System type and return 500
apix.HandleError(c, http.StatusInternalServerError, nil, errors.Sys(err.Error()))
return
}
apix.HandleData(c, http.StatusOK, user, nil)
}
Routing Typed Errors with Handle
Use Handle when you already have an *errors.Error (such as the custom types defined earlier) and want the framework to determine the response format based on the error's type.
func CreateOrder(c *gin.Context) {
if err := svc.ValidateOrder(c.Request.Body); err != nil {
// err is *errors.Error (e.g., ErrInsufficientBalance)
// Handle inspects err.Type: Application errors trigger response.Fail()
apix.Handle(c, http.StatusBadRequest, err)
return
}
orderID, err := svc.Create(c)
if err != nil {
apix.Handle(c, http.StatusInternalServerError, errors.Sys(err.Error()))
return
}
apix.HandleData(c, http.StatusCreated, map[string]string{"id": orderID}, nil)
}
Returning Data with Error Checks
HandleData simplifies the success path by automatically returning the provided data when the error parameter is nil, while still handling error cases consistently.
func DeleteResource(c *gin.Context) {
if !isAdmin(c) {
// Application error with custom code 403
apix.Handle(c, http.StatusForbidden, errors.VerifyCode(403, "admin privilege required"))
return
}
if err := svc.Delete(c.Param("id")); err != nil {
apix.Handle(c, http.StatusInternalServerError, errors.Sys(err.Error()))
return
}
// Empty success payload with 204 status
apix.Handle(c, http.StatusNoContent, nil)
}
Summary
- Define reusable
*errors.Errorinstances using constructors inutils/errors/err.goto categorize failures asSystem,Application, orCoding. - Register the
Recoverymiddleware viahttpx.Startupto capture panics and convert them to typed errors automatically. - Route errors through
apix.Handle,HandleData, orHandleErrorto ensure consistent JSON formatting and appropriate HTTP status codes. - Map custom business codes to HTTP statuses using
VerifyCodeand theCodeInt()method for precise API control.
Frequently Asked Questions
What is the difference between System and Application error types in Gorig?
System errors represent infrastructure failures like database connection timeouts or file system errors, and they automatically trigger HTTP 500 responses via response.ErrorSystem. Application errors represent business logic violations such as validation failures or insufficient permissions, and they use response.Fail to return 4xx status codes with detailed messages. The distinction ensures that internal failures are logged as warnings while user-facing errors are logged as standard errors.
How do I map custom error codes to specific HTTP status codes?
Use the errors.VerifyCode(code, message) constructor to create an Application type error with a numeric code string. When passed to apix.Handle, the framework calls err.CodeInt() to parse the code and overrides the default HTTP status. For example, errors.VerifyCode(404, "not found") will return HTTP 404 even if the default for validation errors is 400.
Can I use native Go errors with Gorig's custom error handlers?
Yes, but you must wrap native errors using errors.Sys(err.Error()) or errors.Verify(err.Error()) before passing them to apix.Handle or HandleError. The HandleError function accepts a native error wrapped in the *errors.Error struct, allowing you to maintain Gorig's consistent JSON response format while handling legacy error sources.
How does panic recovery integrate with custom error types?
The Recovery middleware in httpx/mid.recovery.go catches all panics during request handling and converts them into System type *errors.Error instances. It then calls apix.PanicNotify, which logs the error with stack trace information and returns a generic 500 response to the client without exposing internal details. This ensures that even unhandled runtime errors follow your established error response format.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →