Documentation
¶
Overview ¶
Package response provides structured JSON response helpers with a consistent envelope format and generics support.
The standard response envelope:
{
"success": true,
"message": "User created",
"data": { ... },
"meta": { ... },
"timestamp": 1700000000
}
Usage with the builder:
response.New().
Status(201).
Message("User created").
Data(user).
Send(w)
Or with convenience functions:
response.OK(w, "Success", user) response.NotFound(w, "User not found") response.ValidationError(w, fields)
Index ¶
- func Accepted(w http.ResponseWriter, message string, data any)
- func BadRequest(w http.ResponseWriter, message string)
- func Conflict(w http.ResponseWriter, message string)
- func Created(w http.ResponseWriter, message string, data any)
- func CursorPaginated(w http.ResponseWriter, data any, meta CursorMeta)
- func Err(w http.ResponseWriter, err error)
- func Error(w http.ResponseWriter, statusCode int, code, message string)
- func ErrorWithFields(w http.ResponseWriter, statusCode int, code, message string, ...)
- func File(w http.ResponseWriter, filename string, data []byte, contentType string)
- func Forbidden(w http.ResponseWriter, message string)
- func HTML(w http.ResponseWriter, statusCode int, html string)
- func Handle(fn HandlerFunc) http.HandlerFunc
- func HandleWith(fn HandlerFunc, ...) http.HandlerFunc
- func IndentedJSON(w http.ResponseWriter, statusCode int, data any)
- func InternalServerError(w http.ResponseWriter, message string)
- func JSON(w http.ResponseWriter, statusCode int, data any)
- func JSONP(w http.ResponseWriter, r *http.Request, statusCode int, data any)
- func NoContent(w http.ResponseWriter)
- func NotFound(w http.ResponseWriter, message string)
- func OK(w http.ResponseWriter, message string, data any)
- func Paginated(w http.ResponseWriter, data any, meta PageMeta)
- func PaginatedWithMessage(w http.ResponseWriter, message string, data any, meta PageMeta)
- func PureJSON(w http.ResponseWriter, statusCode int, data any)
- func Raw(w http.ResponseWriter, statusCode int, contentType string, data []byte)
- func Reader(w http.ResponseWriter, statusCode int, contentType string, contentLength int64, ...)
- func Redirect(w http.ResponseWriter, r *http.Request, url string, code int)
- func ServeContent(w http.ResponseWriter, r *http.Request, content io.ReadSeeker, ...)
- func ServeFile(w http.ResponseWriter, r *http.Request, path string, cfg ContentConfig) error
- func ServiceUnavailable(w http.ResponseWriter, message string)
- func SetLinkHeader(w http.ResponseWriter, baseURL string, page, perPage, total int)
- func Stream(w http.ResponseWriter, fn func(send func(event, data string) error) error)
- func StreamJSON(w http.ResponseWriter, fn func(send func(event string, data any) error) error)
- func Text(w http.ResponseWriter, statusCode int, text string)
- func TooManyRequests(w http.ResponseWriter, message string)
- func Unauthorized(w http.ResponseWriter, message string)
- func ValidationError(w http.ResponseWriter, fields map[string]string)
- func XML(w http.ResponseWriter, statusCode int, data any)
- type Builder
- func (b *Builder) CursorPagination(nextCursor string, hasMore bool, total int) *Builder
- func (b *Builder) Data(data any) *Builder
- func (b *Builder) Err(code, message string) *Builder
- func (b *Builder) ErrDetails(details map[string]any) *Builder
- func (b *Builder) ErrFields(fields map[string]string) *Builder
- func (b *Builder) Header(key, value string) *Builder
- func (b *Builder) JSON() ([]byte, error)
- func (b *Builder) Message(msg string) *Builder
- func (b *Builder) Meta(meta any) *Builder
- func (b *Builder) MustJSON() []byte
- func (b *Builder) Pagination(page, perPage, total int) *Builder
- func (b *Builder) Send(w http.ResponseWriter)
- func (b *Builder) Status(code int) *Builder
- type ContentConfig
- type CursorMeta
- type Envelope
- type ErrorBody
- type HandlerFunc
- type PageMeta
- type TypedEnvelope
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Accepted ¶
func Accepted(w http.ResponseWriter, message string, data any)
Accepted writes a 202 response.
func BadRequest ¶
func BadRequest(w http.ResponseWriter, message string)
BadRequest writes a 400 response.
func Conflict ¶
func Conflict(w http.ResponseWriter, message string)
Conflict writes a 409 response.
func Created ¶
func Created(w http.ResponseWriter, message string, data any)
Created writes a 201 response.
func CursorPaginated ¶
func CursorPaginated(w http.ResponseWriter, data any, meta CursorMeta)
CursorPaginated writes a cursor-paginated response.
response.CursorPaginated(w, events, response.CursorMeta{
NextCursor: "eyJpZCI6MTAwfQ==",
HasMore: true,
Total: 500,
})
func Err ¶
func Err(w http.ResponseWriter, err error)
Err writes an error response from an *errors.Error. This is the primary way to send error responses, bridging the errors package.
if err != nil {
response.Err(w, err)
return
}
func Error ¶
func Error(w http.ResponseWriter, statusCode int, code, message string)
Error writes a custom error response.
func ErrorWithFields ¶
func ErrorWithFields(w http.ResponseWriter, statusCode int, code, message string, fields map[string]string)
ErrorWithFields writes an error response with field-level errors.
func File ¶
func File(w http.ResponseWriter, filename string, data []byte, contentType string)
File sends a file as a download.
The whole body is written in one shot, so File does not support Range requests. Use ServeContent for media a client needs to seek within, or for resumable downloads.
func Forbidden ¶
func Forbidden(w http.ResponseWriter, message string)
Forbidden writes a 403 response.
func HTML ¶
func HTML(w http.ResponseWriter, statusCode int, html string)
HTML writes an HTML response.
func Handle ¶
func Handle(fn HandlerFunc) http.HandlerFunc
Handle wraps a HandlerFunc into a standard http.HandlerFunc. If the handler returns an error:
- *errors.Error → sends the appropriate status code and error body
- Standard error → sends 500 with a generic message (original error NOT exposed)
func HandleWith ¶
func HandleWith(fn HandlerFunc, onError func(w http.ResponseWriter, r *http.Request, err error)) http.HandlerFunc
HandleWith wraps a HandlerFunc with a custom error handler.
func IndentedJSON ¶ added in v0.5.0
func IndentedJSON(w http.ResponseWriter, statusCode int, data any)
IndentedJSON writes a pretty-printed JSON response (no envelope). Useful for debug endpoints or human-readable API responses.
func InternalServerError ¶
func InternalServerError(w http.ResponseWriter, message string)
InternalServerError writes a 500 response.
func JSON ¶
func JSON(w http.ResponseWriter, statusCode int, data any)
JSON writes a response with the given status code and data. The success flag is derived from the status code (2xx is treated as success) so the envelope stays consistent even when a non-2xx status is passed.
func JSONP ¶ added in v0.5.0
JSONP writes a JSONP response for cross-domain callbacks. The callback name is read from the "callback" query parameter. If no callback is provided, it falls back to a regular JSON response. The callback name is validated to prevent XSS injection.
func NoContent ¶
func NoContent(w http.ResponseWriter)
NoContent writes a 204 response with no body.
func NotFound ¶
func NotFound(w http.ResponseWriter, message string)
NotFound writes a 404 response.
func Paginated ¶
func Paginated(w http.ResponseWriter, data any, meta PageMeta)
Paginated writes a paginated response with offset-based metadata.
response.Paginated(w, users, response.PageMeta{
Page: 1, PerPage: 20, Total: 150, TotalPages: 8,
})
func PaginatedWithMessage ¶
func PaginatedWithMessage(w http.ResponseWriter, message string, data any, meta PageMeta)
PaginatedWithMessage writes a paginated response with a message.
func PureJSON ¶ added in v0.5.0
func PureJSON(w http.ResponseWriter, statusCode int, data any)
PureJSON writes a JSON response without HTML escaping of <, >, and &. Standard json.Encoder escapes these characters for safe embedding in HTML; PureJSON preserves them as-is for clients that consume raw JSON.
func Raw ¶
func Raw(w http.ResponseWriter, statusCode int, contentType string, data []byte)
Raw writes raw bytes with a custom content type. Use this for non-JSON responses like HTML, XML, plain text, etc.
func Reader ¶ added in v0.5.0
func Reader(w http.ResponseWriter, statusCode int, contentType string, contentLength int64, reader io.Reader)
Reader streams data from an io.Reader to the response. Useful for proxying responses or sending large files without loading them into memory.
The stream is written start to finish, so Reader does not support Range requests. If the source can seek, use ServeContent instead.
func ServeContent ¶ added in v0.26.0
func ServeContent(w http.ResponseWriter, r *http.Request, content io.ReadSeeker, cfg ContentConfig)
ServeContent serves an io.ReadSeeker with full HTTP Range support, so it can back media that the client needs to seek within (a <video> or <audio> element) as well as resumable downloads.
f, err := os.Open(path)
if err != nil {
return errors.NotFound("File")
}
defer f.Close()
response.ServeContent(w, r, f, response.ContentConfig{
Filename: "clip.mp4",
Inline: true,
ModTime: info.ModTime(),
})
Range requests, conditional GETs (If-None-Match, If-Modified-Since, If-Range), HEAD, and 416 Range Not Satisfiable are all handled. Unlike Reader, which streams a response start to finish, this seeks within the content to serve the requested byte range.
The caller is responsible for closing the content if it needs closing.
func ServeFile ¶ added in v0.26.0
func ServeFile(w http.ResponseWriter, r *http.Request, path string, cfg ContentConfig) error
ServeFile opens the file at path and serves it with ServeContent, defaulting ModTime to the file's modification time and detecting the content type from its extension.
response.Handle(func(w http.ResponseWriter, r *http.Request) error {
return response.ServeFile(w, r, "/srv/media/"+id+".mp4", response.ContentConfig{
Inline: true,
CacheControl: "public, max-age=86400",
})
})
A missing file becomes a 404 and a directory a 404; any other failure is a 500 wrapping the underlying error. Nothing is written to w when an error is returned, so the caller (or response.Handle) can render it.
path is trusted. Never build it directly from user input — a request-supplied segment must be validated, or joined with filepath.Join and confirmed to still live under the intended root, or an attacker can traverse out of it.
func ServiceUnavailable ¶
func ServiceUnavailable(w http.ResponseWriter, message string)
ServiceUnavailable writes a 503 response.
func SetLinkHeader ¶ added in v0.2.0
func SetLinkHeader(w http.ResponseWriter, baseURL string, page, perPage, total int)
SetLinkHeader sets an RFC 5988 Link header for paginated responses.
response.SetLinkHeader(w, "https://api.example.com/users", 2, 20, 100) // Link: <https://api.example.com/users?page=3&per_page=20>; rel="next", // <https://api.example.com/users?page=1&per_page=20>; rel="prev", // <https://api.example.com/users?page=1&per_page=20>; rel="first", // <https://api.example.com/users?page=5&per_page=20>; rel="last"
func Stream ¶
func Stream(w http.ResponseWriter, fn func(send func(event, data string) error) error)
Stream provides Server-Sent Events (SSE) streaming.
response.Stream(w, func(send func(event, data string) error) error {
for msg := range messages {
if err := send("message", msg); err != nil {
return err
}
}
return nil
})
func StreamJSON ¶
StreamJSON is like Stream but sends JSON-encoded data.
func Text ¶
func Text(w http.ResponseWriter, statusCode int, text string)
Text writes a plain text response.
func TooManyRequests ¶
func TooManyRequests(w http.ResponseWriter, message string)
TooManyRequests writes a 429 response.
func Unauthorized ¶
func Unauthorized(w http.ResponseWriter, message string)
Unauthorized writes a 401 response.
func ValidationError ¶
func ValidationError(w http.ResponseWriter, fields map[string]string)
ValidationError writes a 422 response with field errors.
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder provides a fluent interface for constructing responses.
response.New().
Status(http.StatusCreated).
Message("User created").
Data(user).
Header("X-Resource-ID", user.ID).
Send(w)
func New ¶
func New() *Builder
New creates a new response Builder with defaults (200 OK, success=true).
func (*Builder) CursorPagination ¶
CursorPagination sets cursor-based pagination metadata.
func (*Builder) ErrDetails ¶
ErrDetails adds arbitrary error metadata.
func (*Builder) Pagination ¶
Pagination sets offset-based pagination metadata.
func (*Builder) Send ¶
func (b *Builder) Send(w http.ResponseWriter)
Send writes the response to the http.ResponseWriter. The timestamp is set at send time to reflect the actual response time.
type ContentConfig ¶ added in v0.26.0
type ContentConfig struct {
// Filename is used for content-type detection (by extension) and, when
// non-empty, sets a Content-Disposition header naming the download.
Filename string
// Inline serves the content with an "inline" Content-Disposition rather
// than "attachment", so browsers render it in place instead of
// downloading it. Only applies when Filename is set.
Inline bool
// ContentType overrides detection. When empty, the type is derived from
// Filename's extension, falling back to sniffing the first 512 bytes.
ContentType string
// ModTime is the content's last modification time. When non-zero it is
// sent as Last-Modified and used to answer If-Modified-Since and
// If-Range requests.
ModTime time.Time
// ETag identifies this version of the content and is used to answer
// If-None-Match and If-Range requests. Quotes are added if missing, so
// both `abc123` and `"abc123"` are accepted; a weak validator must be
// written in full as `W/"abc123"`.
ETag string
// CacheControl sets the Cache-Control header, e.g. "public, max-age=3600".
// When empty the header is not set.
CacheControl string
}
ContentConfig configures ServeContent and ServeFile.
Every field is optional; the zero value serves the content with a sniffed content type, no caching headers, and no Content-Disposition.
type CursorMeta ¶
type CursorMeta struct {
NextCursor string `json:"next_cursor,omitempty"`
HasMore bool `json:"has_more"`
Total int `json:"total,omitempty"`
}
CursorMeta represents cursor-based pagination metadata.
type Envelope ¶
type Envelope struct {
Success bool `json:"success"`
Message string `json:"message,omitempty"`
Data any `json:"data,omitempty"`
Error *ErrorBody `json:"error,omitempty"`
Meta any `json:"meta,omitempty"`
Timestamp int64 `json:"timestamp"`
}
Envelope is the standard API response envelope. All API responses are wrapped in this structure.
func (Envelope) MarshalJSON ¶ added in v0.17.0
MarshalJSON renders the envelope, always including the "data" field on success responses (as null when no data is set) while still omitting it on error responses. The struct tag uses omitempty so error envelopes stay clean; this override forces "data": null for successful responses so clients can rely on the field always being present.
type ErrorBody ¶
type ErrorBody struct {
Code string `json:"code"`
Message string `json:"message"`
Fields map[string]string `json:"fields,omitempty"`
Details map[string]any `json:"details,omitempty"`
}
ErrorBody is the error portion of the response envelope.
type HandlerFunc ¶
type HandlerFunc func(w http.ResponseWriter, r *http.Request) error
HandlerFunc is an HTTP handler that returns an error. When the handler returns a non-nil error, it is automatically converted to a structured JSON response.
mux.HandleFunc("GET /users/{id}", response.Handle(getUser))
func getUser(w http.ResponseWriter, r *http.Request) error {
id := request.PathParam(r, "id")
user, err := db.FindUser(id)
if err != nil {
return errors.NotFound("User")
}
response.OK(w, "Success", user)
return nil
}
type PageMeta ¶
type PageMeta struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
Total int `json:"total"`
TotalPages int `json:"total_pages"`
HasNext bool `json:"has_next"`
HasPrevious bool `json:"has_previous"`
}
PageMeta represents offset-based pagination metadata.
func NewPageMeta ¶
NewPageMeta creates a PageMeta from page, perPage, and total count. TotalPages, HasNext, and HasPrevious are computed automatically. If perPage is <= 0, TotalPages defaults to 0.
type TypedEnvelope ¶
type TypedEnvelope[T any] struct { Success bool `json:"success"` Message string `json:"message,omitempty"` Data T `json:"data,omitempty"` Error *ErrorBody `json:"error,omitempty"` Meta any `json:"meta,omitempty"` Timestamp int64 `json:"timestamp"` }
TypedEnvelope is a generic version of Envelope for type-safe responses. Useful when you want compile-time type checking on the data field.