Typed API errors with errors.Is and errors.As
Go
package main
import (
"errors"
"fmt"
"net/http"
)
// APIError carries an HTTP status so handlers can map errors to responses.
type APIError struct {
Status int
Code string
Err error
}
func (e *APIError) Error() string { return fmt.Sprintf("%s: %v", e.Code, e.Err) }
func (e *APIError) Unwrap() error { return e.Err } // errors.Is/As reach the cause
var ErrNotFound = errors.New("not found")
func findUser(id int) error {
return &APIError{Status: http.StatusNotFound, Code: "user_not_found", Err: ErrNotFound}
}
func respond(err error) string {
var apiErr *APIError
if errors.As(err, &apiErr) {
return fmt.Sprintf("http %d (%s)", apiErr.Status, apiErr.Code)
}
if errors.Is(err, ErrNotFound) {
return "http 404 (not found)"
}
return "http 500 (internal error)"
}
func main() {
err := findUser(7)
fmt.Println(err, respond(err), errors.Is(err, ErrNotFound))
}