Files
Dmitry VerkhoturovandUmputun c48254a994 chore(deps): bump go-pkgz/rest to v1.22.0, drop local CORS Vary workaround
v1.22.0 includes the preflight Vary fix (https://github.com/go-pkgz/rest/pull/44):
rest.CORS now adds Vary: Access-Control-Request-Method and
Access-Control-Request-Headers on preflight itself, making the local wrapper
that added them redundant. corsMiddleware now returns rest.CORS directly;
TestCorsMiddleware still asserts those preflight Vary headers, now supplied
upstream.

Also tidies the _example/memory_store module for the new version.
2026-07-03 15:40:10 -05:00

4.2 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What this is

github.com/go-pkgz/rest is a library of HTTP middlewares and small helpers for REST services. It is not an application — there is no main, no server, no CLI. It is consumed by other projects (remark42, etc.). Keep the public API stable and minimal.

Dependencies are deliberately tiny: only stretchr/testify (tests) and golang.org/x/crypto (Argon2/bcrypt for BasicAuth). Do not add a routing framework or logging library dependency — middlewares are plain stdlib and must stay router-agnostic.

Commands

  • Test everything: go test ./...
  • Single test: go test -run TestName ./... (e.g. go test -run TestPing ./...)
  • Race + coverage (mirrors CI): TZ="America/Chicago" go test -timeout=60s -race -covermode=atomic ./...
  • Lint (run from repo root): golangci-lint run --max-issues-per-linter=0 --max-same-issues=0

Time-parsing tests (ParseFromTo) are timezone-sensitive; CI runs with TZ=America/Chicago. Set it locally if a from/to test behaves oddly.

Architecture

Three packages:

  • rest (root) — all middlewares plus JSON/error/file-server helpers.
  • logger — request-logging middleware, split out so it can be wired to any backend via the logger.Backend interface (Logf(format, args...)). Configured with functional options (logger.New(logger.Prefix(...), logger.WithBody, ...)).
  • realiprealip.Get(r) extracts the client IP from proxy headers. Used by both rest.RealIP and the logger package. Only public IPs are accepted from headers.

Middleware conventions

Every middleware is a func(http.Handler) http.Handler (or a func(...) func(http.Handler) http.Handler when it takes config). This is the chi/stdlib-compatible shape — match it for any new middleware. Some middlewares short-circuit the chain (Ping, Health, metrics) by writing a response and returning without calling the next handler.

Configurable middlewares use the functional-options pattern, not option structs:

  • CORS(CorsAllowedOrigins(...), CorsAllowCredentials(true), ...) — options named CorsXxx.
  • Secure(SecFrameOptions(...), SecHSTS(...), ...) — options named SecXxx, plus SecAllHeaders() convenience.
  • logger.New(logger.Prefix(...), ...) — options in logger/options.go.

When adding an option to one of these, follow the existing prefix/naming and keep defaults sensible so calling the constructor with no options is safe.

CSRF build-tag split (important)

CSRF protection has two implementations behind one identical public API, selected by Go version:

  • csrf_go125.go (//go:build go1.25) — thin wrapper over stdlib http.CrossOriginProtection.
  • csrf.go (//go:build !go1.25) — self-contained equivalent for older Go.

NewCrossOriginProtection, AddTrustedOrigin, AddBypassPattern, SetDenyHandler, Check, Handler must exist with the same signatures and behavior in both files. When you change the CSRF API or behavior, edit both files and keep csrf_test.go passing under both build tags. go.mod targets go 1.24, so by default the !go1.25 path compiles unless building with a 1.25 toolchain.

Helpers

rest.go holds the JSON render/encode/decode helpers (RenderJSON, EncodeJSON/DecodeJSON generics, RenderJSONWithHTML) and ParseFromTo. httperrors.go has SendErrorJSON/NewErrorLogger. file_server.go has FileServer (directory listing disabled by design). benchmarks.go keeps an in-memory ring of up to 900 per-second data points (15 min) queried via Stats(duration).

Conventions

  • One test file per source file: foo.gofoo_test.go only. Table-driven with testify. Note depricattion.go/depricattion_test.go is misspelled but is the real filename — don't "fix" it without intent, it's the established path.
  • After changing or adding a middleware/helper, update README.md — it documents every middleware and helper and is the primary user-facing doc.
  • Lint config (.golangci.yml) is strict (govet enable-all, revive, gocritic with performance/style/experimental). modernize is enabled — prefer any over interface{}, slices/maps stdlib, etc.