httpsuite is a Go library for request parsing, response writing, and RFC 9457 problem responses.
v3 keeps the root module stdlib-only and moves validation to an optional submodule.
- Parse JSON request bodies with a default
1 MiBlimit - Return
413 Payload Too Largewhen the configured body limit is exceeded - Bind path params explicitly through a router-specific extractor
- Validate automatically during
ParseRequestwhen a global validator is configured - Keep
ParseRequestpanic-safe for invalid inputs and return regular errors instead - Return consistent RFC 9457 Problem Details
- Write success responses with optional generic metadata
- Support both direct helpers and optional builders
Writing HTTP handlers in Go often leads to repetitive and error-prone code:
- Manual JSON decoding
- Manual validation wiring
- Ad-hoc path parameter extraction
- Inconsistent error handling
- Repeated boilerplate across handlers
httpsuite provides a consistent and safe flow for parsing requests and writing responses, while staying lightweight and idiomatic.
A single entry point replaces multiple manual steps:
req, err := httpsuite.ParseRequest[*MyRequest](w, r, chi.URLParam, nil, "id")
if err != nil {
return
}- JSON decoding
- Path param binding
- Optional validation
All handled in one place, reducing handler complexity.
ParseRequestnever panics on invalid inputs- Handles nil request and nil body safely
- Enforces body size limits with proper HTTP responses
This reduces the risk of runtime crashes and undefined behavior.
Errors follow RFC 9457 Problem Details out of the box.
Instead of ad-hoc responses like:
{ "error": "invalid request" }You get structured, standardized responses:
{
"type": "...",
"title": "...",
"status": 400,
"detail": "..."
}This improves API consistency and client integration.
- Configure once with
SetValidator(...) - Automatically applied during
ParseRequest - Can be overridden per request
This eliminates duplicated validation logic across handlers.
- Core module is stdlib-only
- No framework lock-in
- Validation is optional
You keep full control over your stack.
Choose the level of abstraction you need:
Simple
httpsuite.OK(w, data)Fluent
httpsuite.Reply().Meta(meta).OK(w, data)Advanced
httpsuite.Problem(...).Title(...).Build()Works with:
- Chi
- Gorilla Mux
- net/http
No need to change your router or architecture.
var req MyRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid body", 400)
return
}
idStr := chi.URLParam(r, "id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "invalid id", 400)
return
}
// validation...
// more error handling...Problems:
- Repeated logic across handlers
- Inconsistent error formats
- Easy to miss edge cases
req, err := httpsuite.ParseRequest[*MyRequest](w, r, chi.URLParam, nil, "id")
if err != nil {
return
}
httpsuite.OK(w, req)Benefits:
- Single, consistent flow
- Less code
- Safer defaults
- Chi
- Gorilla Mux
- Go standard
http.ServeMux
go get github.com/rluders/httpsuite/v3go get github.com/rluders/httpsuite/validation/playground- request in:
ParseRequest(...) - success out:
OK(...),Created(...),Reply().Meta(...).OK(...) - problem out:
ProblemResponse(...),NewBadRequestProblem(...),Problem(...).Build() - validation: configure once with
SetValidator(...), override locally withParseOptions.Validator
For simple handlers, prefer direct helpers.
When a handler needs custom headers, metadata, or problem composition, use the optional builders.
ParseRequest never panics on invalid inputs such as a nil request, nil body, or nil path extractor. These cases return regular Go errors so callers can fail safely.
package main
import (
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
"github.com/rluders/httpsuite/v3"
)
type CreateUserRequest struct {
ID int `json:"id"`
Name string `json:"name"`
}
func (r *CreateUserRequest) SetParam(fieldName, value string) error {
if fieldName != "id" {
return nil
}
id, err := strconv.Atoi(value)
if err != nil {
return err
}
r.ID = id
return nil
}
func main() {
router := chi.NewRouter()
router.Post("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
req, err := httpsuite.ParseRequest[*CreateUserRequest](w, r, chi.URLParam, nil, "id")
if err != nil {
return
}
httpsuite.OK(w, req)
})
_ = http.ListenAndServe(":8080", router)
}Try it:
curl -X POST http://localhost:8080/users/123 \
-H "Content-Type: application/json" \
-d '{"name":"Ada"}'validator := playground.NewWithValidator(nil, &httpsuite.ProblemConfig{
BaseURL: "https://api.example.com",
})
httpsuite.SetValidator(validator)
req, err := httpsuite.ParseRequest[*CreateUserRequest](
w,
r,
chi.URLParam,
&httpsuite.ParseOptions{
MaxBodyBytes: 1 << 20,
},
"id",
)httpsuite.OK(w, user)
httpsuite.OKWithMeta(w, users, httpsuite.NewPageMeta(page, pageSize, totalItems))
httpsuite.Created(w, user, "/users/42")
httpsuite.ProblemResponse(w, httpsuite.NewNotFoundProblem("user not found"))httpsuite.Reply().
Meta(httpsuite.NewPageMeta(page, pageSize, totalItems)).
OK(w, users)
httpsuite.Reply().
Header("X-Request-ID", requestID).
Created(w, user, "/users/42")problem := httpsuite.Problem(http.StatusNotFound).
Type(httpsuite.GetProblemTypeURL("not_found_error")).
Title("User Not Found").
Detail("user 42 does not exist").
Instance("/users/42").
Build()
httpsuite.RespondProblem(problem).
Header("X-Trace-ID", traceID).
Write(w)- root module:
github.com/rluders/httpsuite/v3 - optional validation adapter:
github.com/rluders/httpsuite/validation/playground - root stays stdlib-only
- validation is opt-in at bootstrap, automatic at parse time when configured
- response metadata is generic and can use
PageMetaorCursorMeta
- update imports from
v2tov3 - update
ParseRequestcalls to passoptsbeforepathParams - configure validation globally with
httpsuite.SetValidator(...) ParseRequestnow validates automatically when a validator is configured- validator-provided status is respected
- use
ParseOptions.SkipValidationto opt out per call - use
ParseOptions.Validatorto override per call - use
ProblemConfigfor custom problem type URLs
Examples live in examples/.
examples/stdmux: core-only withhttp.ServeMuxexamples/gorillamux: path params with Gorilla Muxexamples/chi: global validation with Chiexamples/restapi: full REST example with pagination and custom problems
- request helpers:
request*.go - response helpers/builders:
response*.go - problem handling:
problem*.go
Supports:
- pushing an existing
v*tag - manual release with semantic version bump
- Improving Request Validation and Response Handling in Go Microservices, about the first released version.
- open an issue
- submit a PR
- add a router example
MIT