Documentation
¶
Overview ¶
Package httpapi holds the request and response plumbing shared by the module's HTTP endpoints, following the API design standard: the standard error format, strict JSON decoding, request IDs, and finding the real client behind a trusted proxy.
Index ¶
- Constants
- Variables
- func Client(r *http.Request, trusted []netip.Prefix) (ip *netip.Addr, userAgent string)
- func Decode(w http.ResponseWriter, r *http.Request, dst any) error
- func RequestIDFrom(ctx context.Context) string
- func WithRequestID(h http.Handler) http.Handler
- func WriteDecodeError(w http.ResponseWriter, r *http.Request, err error)
- func WriteError(w http.ResponseWriter, r *http.Request, status int, code, message string, ...)
- func WriteInternalError(w http.ResponseWriter, r *http.Request, log *slog.Logger, err error)
- func WriteJSON(w http.ResponseWriter, r *http.Request, status int, v any)
- type FieldError
Constants ¶
const ForwardedUserAgentHeader = "X-Forwarded-User-Agent"
ForwardedUserAgentHeader carries the real client's User-Agent from a trusted proxy (Next.js), whose own User-Agent is in User-Agent.
const MaxBodySize = 64 << 10
MaxBodySize is the largest request body the module reads.
const RequestIDHeader = "X-Request-ID"
RequestIDHeader carries the request ID in requests and responses.
Variables ¶
var ( ErrBadRequest = errors.New("malformed request body") ErrTooLarge = errors.New("request body too large") )
Decoding errors, for mapping to responses.
Functions ¶
func Client ¶
Client returns the real client's IP address and user agent.
If the request comes from a trusted proxy, X-Forwarded-For is read from the right, skipping trusted addresses, and the first untrusted one is the client: entries further left were written by the client itself and can't be believed. X-Forwarded-User-Agent is used if present. Requests from anywhere else use the connection's own address and User-Agent. ip is nil if no address can be determined.
func Decode ¶
Decode reads a single JSON object from r's body into dst. Unknown fields and trailing data are rejected, so typos fail loudly instead of being ignored.
func RequestIDFrom ¶
RequestIDFrom returns the request ID set by WithRequestID, or "".
func WithRequestID ¶
WithRequestID wraps h so every request has an ID, available through RequestIDFrom and returned in the X-Request-ID response header. It reuses an ID already set on the response by the project's own middleware, then a well-formed incoming X-Request-ID, and otherwise generates a UUIDv7.
func WriteDecodeError ¶
func WriteDecodeError(w http.ResponseWriter, r *http.Request, err error)
WriteDecodeError writes the response for an error from Decode.
func WriteError ¶
func WriteError(w http.ResponseWriter, r *http.Request, status int, code, message string, details []FieldError)
WriteError writes an error in the standard format. message must be safe to show to users.
func WriteInternalError ¶
WriteInternalError logs err and writes a generic 500. The client never sees err, which may contain internal details.
Types ¶
type FieldError ¶
type FieldError struct {
Field string `json:"field"`
Code string `json:"code"`
Message string `json:"message"`
}
FieldError describes one invalid field in a validation_failed error.