feat(web): httputil.WithStatus — configurable success status on the handler adapters (v1.5.0)

This commit is contained in:
2026-08-12 17:55:28 -06:00
parent 80b28dfcc4
commit bcccfef443
8 changed files with 217 additions and 23 deletions
+15 -8
View File
@@ -1,6 +1,6 @@
# einherjar/web
[![version](https://img.shields.io/badge/version-v1.4.0-5C4EE5?style=flat-square)](https://code.nochebuena.dev/einherjar/web)
[![version](https://img.shields.io/badge/version-v1.5.0-5C4EE5?style=flat-square)](https://code.nochebuena.dev/einherjar/web)
[![license](https://img.shields.io/badge/license-AGPL--3.0-22863A?style=flat-square)](LICENSE)
[![go](https://img.shields.io/badge/Go-1.26+-00ADD8?style=flat-square&logo=go&logoColor=white)](https://go.dev)
@@ -150,26 +150,33 @@ type CreateUserRes struct {
v := valid.New()
// POST /users — decode body → validate → call service → encode response
srv.Post("/users", httputil.Handle(v, func(ctx context.Context, req CreateUserReq) (CreateUserRes, error) {
// POST /users — decode → validate → call → encode. WithStatus makes it 201 Created.
srv.Post("/users", httputil.Handle(v, logger, func(ctx context.Context, req CreateUserReq) (CreateUserRes, error) {
id, err := userService.Create(ctx, req.Email, req.Name)
if err != nil {
return CreateUserRes{}, err
}
return CreateUserRes{ID: id}, nil
}))
}, httputil.WithStatus(http.StatusCreated)))
// GET /users/{id} — no request body
srv.Get("/users/{id}", httputil.HandleNoBody(func(ctx context.Context) (CreateUserRes, error) {
// GET /users/{id} — no request body (defaults to 200)
srv.Get("/users/{id}", httputil.HandleNoBody(logger, func(ctx context.Context) (CreateUserRes, error) {
// ...
}))
// DELETE /users/{id} — no response body
srv.Delete("/users/{id}", httputil.HandleEmpty(v, func(ctx context.Context, req DeleteReq) error {
// DELETE /users/{id} — no response body (defaults to 204)
srv.Delete("/users/{id}", httputil.HandleEmpty(v, logger, func(ctx context.Context, req DeleteReq) error {
return userService.Delete(ctx, req.ID)
}))
```
**Success status** defaults to 200 for the body-returning adapters and 204 for `HandleEmpty`;
override it with `httputil.WithStatus(code)` — e.g. `WithStatus(http.StatusCreated)` on a POST, or
`WithStatus(http.StatusAccepted)` for an async `HandleEmpty`. The code must be 2xx (these adapters
own only the success path); a non-2xx code panics at wiring, so the service fails to start rather
than emit a wrong status at runtime. **Error status is separate** — return the right `*xerrors.Err`
and `Error` maps it (full 16-code table below).
Validation failures return 400 with a structured JSON error. All `*xerrors.Err`
values are mapped to their canonical HTTP status codes (full 16-code table below).