docs(mcp): document httputil handlers + WithStatus in wire builtin (v1.3.3)
This commit is contained in:
@@ -6,6 +6,21 @@ This module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html
|
||||
|
||||
---
|
||||
|
||||
## [1.3.3] — 2026-08-09
|
||||
|
||||
Patch. Documents the httputil handler adapters and configurable success status.
|
||||
|
||||
### Added
|
||||
|
||||
- Wire builtin gains an "HTTP handlers (httputil)" section — `Handle`/`HandleNoBody`/
|
||||
`HandleEmpty`, their default success statuses (200/200/204), and `httputil.WithStatus`
|
||||
(web v1.5.0) for 201 Created / 202 Accepted, with the 2xx-only panic-at-wiring rule and
|
||||
automatic error mapping.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Wire builtin feature-hook example passes `logger` to the handler constructor (httputil needs it).
|
||||
|
||||
## [1.3.2] — 2026-08-08
|
||||
|
||||
Patch. Scaffold/wire middleware order now mirrors `web.New`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# einherjar/mcp
|
||||
|
||||
[](https://code.nochebuena.dev/einherjar/mcp)
|
||||
[](https://code.nochebuena.dev/einherjar/mcp)
|
||||
[](LICENSE)
|
||||
[](https://go.dev)
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import (
|
||||
|
||||
const (
|
||||
serverName = "einherjar-mcp"
|
||||
serverVersion = "v1.3.2"
|
||||
serverVersion = "v1.3.3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
+44
-34
File diff suppressed because one or more lines are too long
@@ -348,7 +348,7 @@ func withUsers(
|
||||
repo := userrepo.New(db)
|
||||
uow := postgres.NewUnitOfWork(logger, db)
|
||||
svc := usersvc.New(repo, uow)
|
||||
h := userhandler.New(svc, v)
|
||||
h := userhandler.New(svc, v, logger) // handler carries v + logger for httputil
|
||||
|
||||
// Literal-segment routes register BEFORE parametrised siblings.
|
||||
srv.Put("/api/v1/users/me/password", h.ChangeOwnPassword)
|
||||
@@ -384,6 +384,53 @@ srv.Put("/api/v1/users/{user_id}", h.UpdateUser)
|
||||
srv.Put("/api/v1/users/me/password", h.ChangeOwnPassword)
|
||||
```
|
||||
|
||||
## HTTP handlers (httputil)
|
||||
|
||||
Handler methods (`h.CreateUser`, `h.ListUsers`, …) wrap `web/httputil`, which does the
|
||||
decode → validate → call → encode. The method body is just the typed business call:
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.nochebuena.dev/einherjar/web/httputil"
|
||||
)
|
||||
|
||||
// POST that creates a resource → 201 Created via WithStatus.
|
||||
func (h *Handler) CreateUser(w http.ResponseWriter, r *http.Request) {
|
||||
httputil.Handle(h.v, h.logger, func(ctx context.Context, req CreateUserReq) (CreateUserRes, error) {
|
||||
id, err := h.svc.Create(ctx, req)
|
||||
if err != nil {
|
||||
return CreateUserRes{}, err // xerror → HTTP status is automatic (see below)
|
||||
}
|
||||
return CreateUserRes{ID: id}, nil
|
||||
}, httputil.WithStatus(http.StatusCreated))(w, r)
|
||||
}
|
||||
```
|
||||
|
||||
**Adapters and their default success status:**
|
||||
|
||||
| Adapter | Body | Default | Use for |
|
||||
|---|---|---|---|
|
||||
| `httputil.Handle(v, logger, fn, opts…)` | req + res | **200** | POST/PUT returning a body |
|
||||
| `httputil.HandleNoBody(logger, fn, opts…)` | res only | **200** | GET/HEAD |
|
||||
| `httputil.HandleEmpty(v, logger, fn, opts…)` | req only | **204** | DELETE / body-less PUT |
|
||||
|
||||
**Override the success status with `httputil.WithStatus(code)`** — `WithStatus(http.StatusCreated)`
|
||||
(201) on a create, `WithStatus(http.StatusAccepted)` (202) on an async `HandleEmpty`. The code **must
|
||||
be 2xx**: these adapters own only the success path, so a non-2xx code is a routing mistake and
|
||||
**panics at wiring** (the service fails to boot rather than emit a wrong status at runtime). Never
|
||||
pass a 4xx/5xx to `WithStatus`.
|
||||
|
||||
**Error status is automatic — never set it by hand.** Return the right `*xerrors.Err`
|
||||
(`xerrors.NotFound(…)`, `xerrors.InvalidInput(…)`, `xerrors.PermissionDenied(…)`, …) and
|
||||
`httputil.Error` maps it to the HTTP status and logs at the derived level (5xx→Error, 4xx→Warn,
|
||||
499→Info). Validation failures become 400 automatically.
|
||||
|
||||
**Escape hatch:** for a response the adapters don't cover, write a raw `http.HandlerFunc` and call
|
||||
`httputil.JSON(w, status, v)` or `httputil.NoContent(w)` yourself — that is the only place you pass
|
||||
a status literal.
|
||||
|
||||
## Authorization
|
||||
|
||||
Every protected route registers with `.With(authz(provider, resource, grant))`:
|
||||
|
||||
Reference in New Issue
Block a user