README ¶
errs: Enhanced Error Handling for Go
errs is a Go package that provides enhanced error handling capabilities for Go applications, especially suited for web services and APIs. It introduces structured errors with customizable slugs, HTTP status code mapping, detailed error messages, and support for Problem Details for HTTP APIs (RFC 7807).
Features
Custom Error Types: Define errors with unique slugs for easy identification. HTTP Status Code Mapping: Automatically convert errors into appropriate HTTP status codes. Detailed Error Information: Attach detailed error messages and custom parameters to errors. Problem Details Support: Generate error responses conforming to RFC 9457 for RESTful APIs.
Installation
To install errs, use the go get command:
go get github.com/valensto/errs
Ensure you have Go installed and your workspace is properly set up.
Usage Here's a simple example to get started with errs:
package main
import (
"fmt"
"github.com/valensto/errs"
"net/http"
)
func main() {
// Create a new error
err := errs.New(errs.SlugNotFound, "User not found")
// Convert the error into an HTTP status code
statusCode := errs.HTTPStatus(err)
fmt.Printf("HTTP Status Code: %d\n", statusCode)
// Create a Problem Details response
problemDetails := errs.ProblemJSON(err, "/path/where/error/occurred", errs.BlankType)
fmt.Printf("Problem Details: %+v\n", problemDetails)
}
Good practices
Every package that can return errors should create a errors.go file looking like this :
package xxx
import "github.com/valensto/errs"
var (
SlugPhoneInvalid errs.BadRequest = "phone_invalid"
SlugEmailInvalid errs.BadRequest = "email_invalid"
)
In the package, you should always return one of these errors.
// DO THIS
if err != nil {
return fmt.Errorf("add more details to the trace: %w", err)
}
if !ok {
return errs.New(SlugPhoneInvalid, "phone is invalid")
}
// DON'T DO THIS
if err != nil {
return err
}
if !ok {
return fmt.Errorf("undefined error")
}
if !ok {
return errors.New("undefined error")
}
This way, the user of your package know what to expect regarding errors.
Note: you can always add details with fmt.Errorf("%w: details", ErrFirst)
Benefits
In your package tests, you can catch every case using errors.Is(err, ErrFirst) or require.ErrorIs(t, err, ErrFirst). You don't have (and shouldn't have) to rely on errors of another package in your tests.
The caller of your package can define different behavior depending on the errors returned.
The root error allows you to trace the execution that lead to the error e.g. invalid request: invalid: wrong format: hex contains invalid characters
Rule of thumb
- never use errors.New()
- never directly return an error that is not exposed by your package
- wrap errors using fmt.Errorf() with the %w syntax
- use the syntax err: subErr for consistency in logs
- use this package errors at the transport layer ; to find the HTTP/GRPC status and return a known slug to the front-end
Documentation For more detailed documentation, refer to the code comments within the package.
Contributing Contributions to errs are welcome!
Support If you have any questions or issues, please open an issue on the GitHub repository, or contact me directly at hi@valensto.com
Documentation ¶
Overview ¶
Package errs provides a simple error handling mechanism based with slug tailored for web applications. It defines custom error types for various HTTP status codes and offers a utility function to convert these errors into their corresponding HTTP status codes. This approach simplifies error handling across the HTTP handlers, ensuring consistent responses and facilitating easier debugging.
Index ¶
- Constants
- func DetailFromErr(err error) string
- func HTTPStatus(err error) int
- func ParamsFromErr(err error) map[string]string
- func ProblemJSON(err error, instance string, pbType ...ProblemJSONType) map[string]any
- type Duplicate
- type Err
- type Forbidden
- type Internal
- type Invalid
- type NotFound
- type NotImplemented
- type Params
- type ProblemJSONType
- type Slug
- type Unauthorized
- type Unknown
Constants ¶
const ( SlugUnknown Unknown = "unknown" SlugNotFound NotFound = "not-found" SlugInvalid Invalid = "request-invalid" SlugForbidden Forbidden = "forbidden" SlugDuplicate Duplicate = "already-exists" SlugNotImplemented NotImplemented = "not-implemented" SlugInternal Internal = "internal-error" )
Constants for each slug type, providing clear and concise identifiers for common error conditions. You can add more slugs as needed to cover additional error cases in your application based on slug types.
Variables ¶
This section is empty.
Functions ¶
func DetailFromErr ¶
DetailFromErr extracts the detailed error message from a given error, if the error is of type Err and contains detailed information. This allows for the retrieval of additional error context useful for logging or displaying to an end user. If the error does not contain detailed information, a default "unknown error" message is returned.
func HTTPStatus ¶
HTTPStatus takes an error object as input and returns the corresponding HTTP status code. It works by unwrapping the provided error to its base type and then matching it against a set of predefined errors. Each predefined error is associated with a specific HTTP status code, allowing for clear and concise error handling in web applications. If the error does not match any of the predefined types, HTTPStatus defaults to returning http.StatusInternalServerError, indicating an unexpected condition.
Usage:
err := someOperation() if err != nil { statusCode := errs.HTTPStatus(err) // Use statusCode for setting HTTP response status }
This function is essential for converting internal error types into appropriate HTTP responses, thereby encapsulating the error handling logic and promoting a cleaner and more maintainable codebase.
func ParamsFromErr ¶
ParamsFromErr extracts the Params map from a given error, if the error is of type Err and contains a Params map. This is particularly useful for errors that include field-specific validation messages or other metadata that might influence how an error is handled or displayed. If the error does not contain a Params map, nil is returned.
func ProblemJSON ¶
func ProblemJSON(err error, instance string, pbType ...ProblemJSONType) map[string]any
ProblemJSON constructs a map representing a Problem Details object as specified by RFC 9457. This standard provides a way to carry machine-readable details of errors in a HTTP response to avoid the need to define new error response formats for HTTP APIs. The function takes an error, an instance URI that identifies the specific occurrence of the problem, and a ProblemJSONType indicating the type of problem.
The resulting map includes the following fields: - type: A URI reference (ProblemJSONType) that identifies the problem type. - title: A short, human-readable summary of the problem type represented by the slug extracted from the error. - status: The HTTP status code generated from the error. - detail: A human-readable explanation specific to this occurrence of the problem. - instance: A URI reference that identifies the specific occurrence of the problem. Additional fields, like 'params', provide further details about the problem when available.
Usage:
err := someOperation() problemDetails := errs.ProblemJSON(err, "http://example.com/err/1234", errs.BlankType) // Use problemDetails to construct the HTTP response body.
Types ¶
type Duplicate ¶
type Duplicate slug
Duplicate represents a 409 Conflict error, indicating a duplicate resource or action.
type Err ¶
type Err struct {
// contains filtered or unexported fields
}
Err represents a detailed error type in the errs package. It contains a Slug indicating the type of error, an underlying error, optional detailed messages, and a Params map to hold additional error information. This structure allows for rich error descriptions and easy translation or formatting for end users.
func New ¶
New creates a new Err instance based on the given Slug and optional details. The Slug represents the specific type of error, while the details provide further context or information about the error condition. This function is a primary entry point for creating typed errors in applications.
func NewFromError ¶
NewFromError creates a new Err instance from a generic error object. It attempts to preserve the error type if it is already a typed Err; otherwise, it wraps the error in a new Err with the SlugUnknown type. This function facilitates error conversion and propagation in applications.
func NewFromValidator ¶
func NewFromValidator(err error, translator ut.Translator) Err
NewFromValidator creates a new Err instance from a validation error produced by the validator package. It translates validation error messages using the provided translator and associates them with their corresponding field names in the Params map. This function is particularly useful for handling validation errors in a structured and user-friendly manner.
func (Err) Error ¶
Error returns a string representation of the Err. It combines the underlying error message with any additional details provided. This method satisfies the error interface, allowing Err to be used like any other error object.
func (Err) WithDetails ¶
WithDetails appends additional details to the Err, providing more context about the error. This method helps in conveying precise error information to the end user.
func (Err) WithError ¶
WithError enriches the Err instance with an additional underlying error, allowing for error wrapping and chaining. This method is useful for building a detailed error trace.
func (Err) WithParams ¶
WithParams merges a map of string key-value pairs into the Err's Params, allowing for the association of arbitrary data with the error. This method is useful for attaching detailed error metadata, such as field-specific error messages.
type Forbidden ¶
type Forbidden slug
Forbidden represents a 403 Forbidden error, indicating lack of permission.
type Internal ¶
type Internal slug
Internal represents a 500 Internal Server Error, indicating a server-side error.
type Invalid ¶ added in v1.0.2
type Invalid slug
Invalid represents a 400 Bad Request error, typically used for validation failures.
type NotImplemented ¶
type NotImplemented slug
NotImplemented represents a 501 Not Implemented error, for unimplemented functionality.
func (NotImplemented) Error ¶
func (s NotImplemented) Error() string
type Params ¶
Params represents a map of string key-value pairs. It is used to associate additional information with errors, such as field names and validation messages in the context of web requests. Params provides methods to manipulate these key-value pairs, making it easier to add, check, or retrieve error-related information.
func NewParams ¶
func NewParams() Params
NewParams creates and returns a new instance of Params. It is a convenience function to initialize Params without needing to manually specify its type.
type ProblemJSONType ¶
type ProblemJSONType string
ProblemJSONType defines a type for representing the "type" field in a Problem Details object. According to RFC 9457, the "type" field is a URI reference that identifies the problem type. It provides a means to give clients more information about the error in a machine-readable format.
const ( // BlankType is a predefined ProblemJSONType representing a general problem type as specified by // RFC 9457. It indicates that the problem does not match any other predefined problem types. BlankType ProblemJSONType = "about:blank" )
type Slug ¶
type Slug interface {
Error() string
}
Slug is an interface that all error slugs implement, providing a basic Error method. It allows for a unified way of handling different types of errors through their slug representations.
func SlugFromErr ¶
SlugFromErr extracts the Slug from a given error, if the error is of type Err and contains a slug. This function is useful for determining the type of an error when handling it, especially in situations where the specific error type influences the application's response. If the error does not contain a slug, SlugUnknown is returned as a fallback.
type Unauthorized ¶
type Unauthorized slug
Unauthorized represents a 401 Unauthorized error, indicating missing or invalid authentication.
func (Unauthorized) Error ¶
func (s Unauthorized) Error() string