Skip to content
Back to skills

Nethttp Architect

ASecurity

Framework-specific delta on rest-api-architect — Go stdlib net/http (Go 1.22+ ServeMux, no router) on Go 1.27.3. Feature layout, struct-tag validation, RFC 9457 errors, JWT or IdP auth, graceful shutdown, OpenAPI via kin-openapi. Read rest-api-architect first for the cross-cutting REST conventions. Use when scaffolding or reviewing a stdlib net/http service.

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added November 7, 2026
developmentgosqlfastapitestingrefactoringapidatabase

Works with

  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add ralvarez-ai/ralvaskills --skill nethttp-architect --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Nethttp Architect?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Nethttp Architect
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ralvarez-ai-nethttp-architect/badge)](https://www.skillsdirectory.com/skills/ralvarez-ai-nethttp-architect)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: nethttp-architect
version: 1.1.0
description: Framework-specific delta on rest-api-architect — Go stdlib net/http (Go 1.22+ ServeMux, no router) on Go 1.27.3. Feature layout, struct-tag validation, RFC 9457 errors, JWT or IdP auth, graceful shutdown, OpenAPI via kin-openapi. Read rest-api-architect first for the cross-cutting REST conventions. Use when scaffolding or reviewing a stdlib net/http service.
---

# net/http Architecture

Targets the stdlib **`net/http`** on **Go 1.27.3**, using the Go 1.22+ enhanced `ServeMux` (method matching, path variables, host matching) — no router framework. Companion to [go-architect](../../languages/go-architect/SKILL.md), [rest-api-architect](../../protocols/rest-api-architect/SKILL.md), and [sql-architect](../../databases/sql-architect/SKILL.md). Implementation skeletons in [RECIPES.md](RECIPES.md); pinned deps in [STACK.md](STACK.md).

## 1. Project structure — feature-based

Same shape as `gin-architect` and `fastapi-architect`: one folder per bounded context under `internal/`, each with `handlers.go`, `service.go`, `repo.go`, `dto.go`, `queries/`. Full tree in [RECIPES.md](RECIPES.md).

- **`handlers.go`** depends on `service.go`; never reaches into `repo.go` directly.
- **`service.go`** is pure Go — no `net/http` imports. Easy to unit-test.
- **`dto.go`** holds wire types; never reuse domain structs as DTOs.
- **Keep the feature folder scannable, not minimal.** Split on a real responsibility seam, not a file count — a subfolder here is a new package (import path + exported surface). See [code-design-refactor §4](../../refactoring/code-design-refactor/SKILL.md#4-extract).

## 2. Routing — `http.ServeMux` with method patterns (Go 1.22+)

The stdlib `ServeMux` supports method matching, path wildcards (`{id}`), and host matching. No third-party router needed. Registration skeleton in [RECIPES.md](RECIPES.md).

- **Method patterns:** `"GET /v1/users/{id}"` — method + path in one string. Mismatched methods auto-return `405 Method Not Allowed`.
- **Path variables:** `id := r.PathValue("user_id")` — typed parsing happens in your handler.
- **Trailing slash:** `"GET /v1/users/"` matches everything under the prefix; `"GET /v1/users/{$}"` matches only the exact path. Be explicit.
- **No "group" abstraction in stdlib** — write the prefix per route or use a small helper. Don't reach for a router framework just for syntactic sugar.

## 3. Request validation

`go-playground/validator` (canonical per [go-architect](../../languages/go-architect/SKILL.md#11-dependencies--logging)) — bind via `json.NewDecoder(r.Body).Decode(&req)` then `validate.Struct(&req)`.

```go
type CreateUserReq struct {
    Email    string `json:"email"    validate:"required,email"`
    Password string `json:"password" validate:"required,min=12"`
}
```

- **`json.Decoder.DisallowUnknownFields()`** at decoder construction — equivalent of Pydantic's `extra="forbid"`. Reject unknown fields rather than silently dropping them.
- **One shared `*validator.Validate`** stored on the handler, not per-request — instantiation is expensive.
- **Limit body size:** wrap `r.Body` with `http.MaxBytesReader(w, r.Body, 1<<20)` before decode — prevents memory exhaustion from oversized payloads.

## 4. Response shaping

A tiny helper to serialise JSON consistently — every handler uses it.

```go
func writeJSON(w http.ResponseWriter, status int, v any) {
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.WriteHeader(status)
    if v != nil {
        _ = json.NewEncoder(w).Encode(v)
    }
}
```

- **Set headers before `WriteHeader`** — once headers are sent, you can't change them.
- **`http.StatusNoContent`** for empty responses (skip `writeJSON` body).
- **Return DTOs, not domain types.** Same discipline as gin-architect.

## 5. Dependency injection

Explicit constructors. Dependencies wired in `main.go`; `*Deps` is passed where needed. For larger graphs, `uber-go/fx` (canonical per go-architect). Hand-wired is clearer for small services.

## 6. Lifespan & graceful shutdown

Same as gin-architect §6; the server is `http.Server`, not `*gin.Engine`. `signal.NotifyContext` for shutdown trigger; the cause records which signal (Go 1.26). All four timeouts (`ReadHeaderTimeout`, `ReadTimeout`, `WriteTimeout`, `IdleTimeout`) are **mandatory in production** — defaults are "unlimited," which is a DoS vector. Full skeleton in [RECIPES.md](RECIPES.md).

## 7. Middleware — function wrapping

stdlib has no middleware abstraction. The standard pattern: a function that takes `http.Handler` and returns `http.Handler`. Compose with a tiny `Chain` helper (in [RECIPES.md](RECIPES.md)).

- **Outermost first** in `Chain` — request id first so every response and log line carries it; logger outside Recover so panics are logged as 500s.
- **Standard chain order:** RequestID → SLog → Recover → CORS → GZip.
- **Auth is per-route, not global** — apply `AuthRequired` only where needed (see §8).
- **Never write your own logger middleware that calls `log.Printf`** — use `slog` (per go-architect).

## 8. Authentication & authorization

**Patterns** live in [rest-api-architect/AUTH_PATTERNS.md](../../protocols/rest-api-architect/AUTH_PATTERNS.md). stdlib specifics:

- **Pattern A — in-house JWT** via `golang-jwt/jwt/v5` + `argon2`. Middleware skeleton in [RECIPES.md](RECIPES.md).
- **Pattern B — external IdP**: `jwt.ParseWithClaims` with a `Keyfunc` that resolves keys via cached JWKS. Verify `aud` and `iss` explicitly.
- **Authorization per route, never global** — `RequireScope` middleware composed with `AuthRequired` (see [RECIPES.md](RECIPES.md)). When the wrap chain gets ugly, build a small helper that takes multiple `Middleware`s and the final handler.

## 9. Error handling — RFC 9457

Same `problem` package shape as `gin-architect`. Helpers take `http.ResponseWriter` + `*http.Request` instead of `*gin.Context`. Writer skeleton in [RECIPES.md](RECIPES.md). The `Recover` middleware catches panics and emits a 500 problem with the correlation id — never a stack trace.

## 10. Concurrency, not "background tasks"

Same guidance as gin-architect — Go has no FastAPI-style `BackgroundTasks`. Detach with `context.Background()`, log errors, set timeouts.

Snippet in [RECIPES.md](RECIPES.md#concurrency--detached-work).

## 11. Testing

`httptest.NewRecorder` + the handler directly — no real socket. Skeleton in [RECIPES.md](RECIPES.md).

- **`testing/synctest`** (per go-architect §9) for any test that depends on goroutine completion timing — deterministic without `time.Sleep`.
- **Table-driven** request-validation tests.

## 12. OpenAPI generation

stdlib `net/http` has no annotation-based generator like `swaggo/swag` (which is Gin-coupled). Two practical options (CI snapshot-tests the spec either way):

- **`getkin/kin-openapi`** — write the OpenAPI 3.1 spec in code, serve it at `/v1/openapi.json`, optionally use it for runtime request validation. The intended choice for stdlib.
- **Hand-written `openapi.yaml`** committed to the repo, served as a static file. Cheapest, but drifts; pair with the OpenAPI snapshot test from [rest-api-architect §15](../../protocols/rest-api-architect/SKILL.md#15-openapi-as-the-source-of-truth).

## When to pick `net/http` over Gin

- **Minimal dependencies** matter (binary size, supply chain, audit).
- **Tight integration with stdlib middleware ecosystem** (third-party `func(http.Handler) http.Handler` chains work everywhere).
- **Forward compatibility** — Go's HTTP server gets faster every release; framework-coupled code lags.
- **Predictable behavior** — no framework-specific surprises (recovery semantics, body re-reads, etc.).

Pick **Gin** when: richer middleware/plugin ecosystem out of the box, team familiar with framework patterns, or you need features Gin provides that aren't in stdlib. Both bundles (`gin` and `nethttp`) are valid; this skill targets `nethttp` projects.

Files in this skill

  • DIGEST.md2.4 KB
  • RECIPES.md6.3 KB
  • SKILL.md7.8 KB
  • STACK.md1.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in development →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.