errs

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 6, 2024 License: MIT Imports: 6 Imported by: 0

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

View Source
const (
	SlugUnknown        Unknown        = "unknown"
	SlugNotFound       NotFound       = "not-found"
	SlugInvalid        Invalid        = "request-invalid"
	SlugUnauthorized   Unauthorized   = "unauthorized"
	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

func DetailFromErr(err error) string

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

func HTTPStatus(err error) int

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

func ParamsFromErr(err error) map[string]string

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.

func (Duplicate) Error

func (s Duplicate) Error() string

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

func New(slug Slug, details ...string) Err

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

func NewFromError(err error) Err

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

func (e Err) Error() string

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

func (e Err) WithDetails(details string) Err

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

func (e Err) WithError(err error) Err

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

func (e Err) WithParams(params map[string]string) Err

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.

func (Forbidden) Error

func (s Forbidden) Error() string

type Internal

type Internal slug

Internal represents a 500 Internal Server Error, indicating a server-side error.

func (Internal) Error

func (s Internal) Error() string

type Invalid added in v1.0.2

type Invalid slug

Invalid represents a 400 Bad Request error, typically used for validation failures.

func (Invalid) Error added in v1.0.2

func (s Invalid) Error() string

type NotFound

type NotFound slug

NotFound represents a 404 Not Found error.

func (NotFound) Error

func (s NotFound) Error() string

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

type Params map[string]string

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.

func (*Params) Add

func (p *Params) Add(key, value string)

Add inserts a key-value pair into the Params map. If the Params map has not been initialized (nil), Add will initialize it before adding the key-value pair. This method ensures that key-value pairs can be safely added to Params even when it is in its zero value state.

func (*Params) IsNil

func (p *Params) IsNil() bool

IsNil checks if the Params map is empty. It returns true if the map contains no key-value pairs, indicating that no additional information has been associated with an error.

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

func SlugFromErr(err error) (s Slug)

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

type Unknown

type Unknown slug

Unknown represents an unspecified error, used as a fallback.

func (Unknown) Error

func (s Unknown) Error() string

Error returns the string representation of the NotFound error slug, facilitating its use as an error object.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL