httperr

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jan 15, 2026 License: MIT Imports: 4 Imported by: 0

README

httperr

Test codecov Go Reference

A Go library for HTTP-aware errors with RFC 7807 Problem Details support.

Installation

go get github.com/paccolamano/httperr

Features

  • HTTP status codes attached to errors
  • Custom error codes for machine-readable error handling
  • Metadata support for additional context
  • RFC 7807 Problem Details JSON serialization
  • Error wrapping compatible with Go 1.13+ errors.Is and errors.As
  • HTTP handler utilities for cleaner error handling

Usage

Creating and Writing Errors
func GetUser(w http.ResponseWriter, r *http.Request) {
    user, err := db.FindUser(id)
    if err != nil {
        httpErr := httperr.HTTP(404, "user not found").
            WithCode(httperr.CodeNotFound).
            WithMetadata("user_id", id)
        httperr.WriteJSON(w, httpErr)
        return
    }
    json.NewEncoder(w).Encode(user)
}

This produces an RFC 7807 compliant JSON response:

{
  "type": "about:blank",
  "title": "Not Found",
  "status": 404,
  "detail": "user not found",
  "code": "NOT_FOUND",
  "user_id": "123"
}
Using the Handler Wrapper

Write handlers that return errors instead of manually handling responses:

func GetUser(w http.ResponseWriter, r *http.Request) error {
    user, err := db.FindUser(id)
    if err != nil {
        return httperr.HTTP(404, "user not found")
    }
    return json.NewEncoder(w).Encode(user)
}

http.Handle("/users/{id}", httperr.Handler(GetUser))
Pre-built Errors

Use common pre-built errors for convenience:

return httperr.ErrNotFound.WithMetadata("resource", "user")
return httperr.ErrUnauthorized
return httperr.ErrValidation.WithMetadata("field", "email")

Examples

See the examples directory for more detailed usage examples.

License

MIT

Documentation

Overview

Package httperr provides HTTP-aware errors with RFC 7807 Problem Details support.

This package simplifies error handling in HTTP APIs by attaching HTTP status codes, custom error codes, and metadata to errors. It supports the standard Go error wrapping interface and provides utilities for writing errors to HTTP responses.

Basic usage:

err := httperr.HTTP(404, "user not found").
	WithCode(httperr.CodeNotFound).
	WithMetadata("user_id", id)
httperr.WriteJSON(w, err)

Using the Handler wrapper:

func GetUser(w http.ResponseWriter, r *http.Request) error {
	user, err := db.FindUser(id)
	if err != nil {
		return httperr.HTTP(404, "user not found")
	}
	return json.NewEncoder(w).Encode(user)
}

http.Handle("/users/{id}", httperr.Handler(GetUser))

Index

Constants

View Source
const (
	// Generic codes
	CodeInternal = "INTERNAL_ERROR"
	CodeUnknown  = "UNKNOWN_ERROR"

	// Validation codes
	CodeValidation    = "VALIDATION_ERROR"
	CodeInvalidInput  = "INVALID_INPUT"
	CodeMissingField  = "MISSING_FIELD"
	CodeInvalidFormat = "INVALID_FORMAT"

	// Resource codes
	CodeNotFound      = "NOT_FOUND"
	CodeAlreadyExists = "ALREADY_EXISTS"
	CodeConflict      = "CONFLICT"

	// Authentication/Authorization codes
	CodeUnauthorized = "UNAUTHORIZED"
	CodeForbidden    = "FORBIDDEN"
	CodeInvalidToken = "INVALID_TOKEN"
	CodeExpiredToken = "EXPIRED_TOKEN"

	// Rate limiting codes
	CodeRateLimited   = "RATE_LIMITED"
	CodeQuotaExceeded = "QUOTA_EXCEEDED"

	// Business logic codes
	CodeBusinessRule = "BUSINESS_RULE_VIOLATION"
	CodePrecondition = "PRECONDITION_FAILED"
)

Common error codes that can be used across applications. These follow a SCREAMING_SNAKE_CASE convention common in error codes.

Variables

View Source
var (
	ErrBadRequest   = New("bad request").WithStatus(400).WithCode(CodeInvalidInput)
	ErrValidation   = New("validation failed").WithStatus(400).WithCode(CodeValidation)
	ErrUnauthorized = New("unauthorized").WithStatus(401).WithCode(CodeUnauthorized)
	ErrForbidden    = New("forbidden").WithStatus(403).WithCode(CodeForbidden)
	ErrNotFound     = New("resource not found").WithStatus(404).WithCode(CodeNotFound)
	ErrConflict     = New("resource conflict").WithStatus(409).WithCode(CodeConflict)
	ErrInternal     = New("internal server error").WithStatus(500).WithCode(CodeInternal)
)

Common pre-built errors with codes for convenience. These can be used directly or as base errors with additional methods chained.

Example:

return httperr.ErrNotFound.WithMetadata("user_id", id)

Functions

func HandlerFunc

func HandlerFunc(h Handler) http.HandlerFunc

HandlerFunc converts a Handler to http.HandlerFunc.

func RecoverHandler

func RecoverHandler(next http.Handler) http.Handler

RecoverHandler is an HTTP middleware that recovers from panics and converts them to 500 errors.

Example:

mux := http.NewServeMux()
mux.HandleFunc("/", myHandler)
http.ListenAndServe(":8080", httperr.RecoverHandler(mux))

func WriteJSON

func WriteJSON(w http.ResponseWriter, err error, opts ...HTTPOption)

WriteJSON writes an error as JSON to an HTTP response. It uses RFC 7807 Problem Details format and sets the appropriate Content-Type header (application/problem+json).

If err is nil, this function does nothing.

Example:

err := httperr.HTTP(404, "user not found").WithCode(httperr.CodeNotFound)
httperr.WriteJSON(w, err)

func WriteText

func WriteText(w http.ResponseWriter, err error)

WriteText writes an error as plain text to an HTTP response. It extracts the HTTP status code from httperr.Error types, or defaults to 500 for other error types.

If err is nil, this function does nothing.

Example:

err := httperr.HTTP(404, "user not found")
httperr.WriteText(w, err)

Types

type Error

type Error struct {
	// contains filtered or unexported fields
}

Error represents an HTTP-aware error with support for status codes, custom error codes, and metadata. It implements the standard error interface and supports wrapping.

func HTTP

func HTTP(status int, msg string) *Error

HTTP creates a new Error with an HTTP status code and message.

func HTTPf

func HTTPf(status int, format string, args ...any) *Error

HTTPf creates a new Error with an HTTP status code and formatted message.

func New

func New(msg string) *Error

New creates a new Error with the given message. This is compatible with errors.New from the standard library.

func Newf

func Newf(format string, args ...any) *Error

Newf creates a new Error with a formatted message.

func Wrap

func Wrap(err error, msg string) *Error

Wrap wraps an existing error with a message. This is similar to fmt.Errorf with %w but returns *Error. If err is nil, returns nil.

func Wrapf

func Wrapf(err error, format string, args ...any) *Error

Wrapf wraps an existing error with a formatted message. If err is nil, returns nil.

func (*Error) Code

func (e *Error) Code() string

Code returns the custom error code.

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface.

func (*Error) HTTPStatus

func (e *Error) HTTPStatus() int

HTTPStatus returns the HTTP status code associated with this error. Returns 500 (Internal Server Error) if no status was set.

func (*Error) Metadata

func (e *Error) Metadata() map[string]any

Metadata returns a copy of the error metadata to prevent external mutation.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the wrapped error (Go 1.13+ error wrapping support).

func (*Error) WithCode

func (e *Error) WithCode(code string) *Error

WithCode returns a new Error with the specified error code. This method is immutable and returns a new instance.

func (*Error) WithMetadata

func (e *Error) WithMetadata(key string, value any) *Error

WithMetadata returns a new Error with the specified metadata merged in. This method is immutable and returns a new instance.

func (*Error) WithStatus

func (e *Error) WithStatus(status int) *Error

WithStatus returns a new Error with the specified HTTP status code. This method is immutable and returns a new instance.

type HTTPOption

type HTTPOption func(*httpConfig)

HTTPOption configures how errors are written to HTTP responses.

func WithTypePrefix

func WithTypePrefix(prefix string) HTTPOption

WithTypePrefix sets a prefix for RFC 7807 type URIs. This is useful for creating domain-specific error type URIs.

Example:

WriteJSON(w, err, WithTypePrefix("https://example.com/errors/"))
// Type becomes: https://example.com/errors/VALIDATION_ERROR

type Handler

type Handler func(w http.ResponseWriter, r *http.Request) error

Handler wraps an http.HandlerFunc that returns an error. It automatically handles error responses using WriteJSON.

This allows writing HTTP handlers that return errors instead of manually writing error responses.

Example:

func GetUser(w http.ResponseWriter, r *http.Request) error {
    user, err := db.FindUser(id)
    if err != nil {
        return httperr.HTTP(404, "user not found")
    }
    json.NewEncoder(w).Encode(user)
    return nil
}

http.Handle("/users/:id", httperr.Handler(GetUser))

func (Handler) ServeHTTP

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

type ProblemDetail

type ProblemDetail struct {
	// Type is a URI reference [RFC3986] that identifies the problem type.
	// When dereferenced, it SHOULD provide human-readable documentation for
	// the problem type. When this member is not present, its value is assumed
	// to be "about:blank".
	Type string `json:"type,omitempty"`

	// Title is a short, human-readable summary of the problem type.
	// It SHOULD NOT change from occurrence to occurrence of the problem,
	// except for purposes of localization.
	Title string `json:"title,omitempty"`

	// Status is the HTTP status code generated by the origin server for this
	// occurrence of the problem.
	Status int `json:"status,omitempty"`

	// Detail is a human-readable explanation specific to this occurrence of
	// the problem.
	Detail string `json:"detail,omitempty"`

	// Instance is a URI reference that identifies the specific occurrence of
	// the problem. It may or may not yield further information if dereferenced.
	Instance string `json:"instance,omitempty"`

	// Extensions contains additional problem-specific fields that are not
	// part of the standard RFC 7807 fields. These will be serialized at the
	// root level of the JSON object.
	Extensions map[string]any `json:"-"`
}

ProblemDetail represents an RFC 7807 Problem Details object. See: https://www.rfc-editor.org/rfc/rfc7807

RFC 7807 defines a "problem detail" as a way to carry machine-readable details of errors in HTTP response content to avoid the need to define new error response formats for HTTP APIs.

func ToProblemDetail

func ToProblemDetail(err error) ProblemDetail

ToProblemDetail converts an error to an RFC 7807 Problem Details object.

If the error is an httperr.Error, it extracts the HTTP status, error code, and metadata. Otherwise, it returns a generic 500 Internal Server Error.

func (ProblemDetail) MarshalJSON

func (p ProblemDetail) MarshalJSON() ([]byte, error)

MarshalJSON implements custom JSON marshaling to include extensions at the root level of the JSON object.

Directories

Path Synopsis
examples
basic command
Package main demonstrates basic usage of httperr for creating and handling HTTP errors.
Package main demonstrates basic usage of httperr for creating and handling HTTP errors.
handler command
Package main demonstrates using httperr.Handler to write cleaner HTTP handlers that return errors.
Package main demonstrates using httperr.Handler to write cleaner HTTP handlers that return errors.
middleware command
Package main demonstrates using httperr middleware for panic recovery and centralized error handling.
Package main demonstrates using httperr middleware for panic recovery and centralized error handling.
rfc7807 command
Package main demonstrates RFC 7807 Problem Details features, including custom type URIs and direct ProblemDetail usage.
Package main demonstrates RFC 7807 Problem Details features, including custom type URIs and direct ProblemDetail usage.
validation command
Package main demonstrates using httperr for request validation with detailed error responses.
Package main demonstrates using httperr for request validation with detailed error responses.
wrapping command
Package main demonstrates error wrapping with httperr, showing how to wrap lower-level errors while adding HTTP context.
Package main demonstrates error wrapping with httperr, showing how to wrap lower-level errors while adding HTTP context.

Jump to

Keyboard shortcuts

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