Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
90cf1aab92
|
|||
|
32831d5a06
|
|||
|
2298846280
|
@@ -5,6 +5,13 @@ All notable changes to this module will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.2] - 2026-05-12
|
||||
|
||||
### Changed
|
||||
|
||||
- `logz` dependency bumped from v0.9.0 to v1.0.1.
|
||||
- `go` directive updated from 1.25 to 1.26.
|
||||
|
||||
## [0.9.0] - 2026-03-18
|
||||
|
||||
### Added
|
||||
@@ -22,4 +29,5 @@ and this module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.
|
||||
- `Recover` intentionally requires no logger injection in this release: it captures the stack trace but does not log it, keeping the middleware usable with zero configuration; logger injection is deferred to a future release.
|
||||
- No middleware is installed by default; the package exports independent functions and the application chooses the chain and order (recommended: `Recover` → `RequestID` → `RequestLogger` → `CORS`).
|
||||
|
||||
[1.0.2]: https://code.nochebuena.dev/go/httpmw/releases/tag/v1.0.2
|
||||
[0.9.0]: https://code.nochebuena.dev/go/httpmw/releases/tag/v0.9.0
|
||||
@@ -11,7 +11,7 @@ authentication or identity logic lives here — see `httpauth-firebase` for that
|
||||
|
||||
## Tier & Dependencies
|
||||
|
||||
**Tier:** 3 (transport layer; depends on Tier 0 `logz`)
|
||||
**Tier:** 2 (transport layer; depends on Tier 1 `logz`)
|
||||
**Module:** `code.nochebuena.dev/go/httpmw`
|
||||
**Direct imports:** `code.nochebuena.dev/go/logz`
|
||||
|
||||
|
||||
97
README.md
Normal file
97
README.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# httpmw
|
||||
|
||||
`net/http` middleware for transport-layer concerns: panic recovery, CORS, request ID injection, and structured request logging.
|
||||
|
||||
## Overview
|
||||
|
||||
Four composable `func(http.Handler) http.Handler` middleware functions:
|
||||
|
||||
| Middleware | Responsibility |
|
||||
|---|---|
|
||||
| `Recover()` | Catches panics; writes 500; captures stack trace |
|
||||
| `RequestID(generator)` | Generates a unique ID; injects it via `logz.WithRequestID`; sets `X-Request-ID` header |
|
||||
| `RequestLogger(logger)` | Logs method, path, status, latency, and request ID after each request |
|
||||
| `CORS(origins)` | Sets CORS headers; short-circuits OPTIONS with 204 |
|
||||
|
||||
No authentication or identity logic lives here — see `httpauth-firebase` for that.
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
go get code.nochebuena.dev/go/httpmw
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
// Recommended order — outermost middleware first
|
||||
mux.Use(httpmw.Recover())
|
||||
mux.Use(httpmw.RequestID(uuid.NewString))
|
||||
mux.Use(httpmw.RequestLogger(logger))
|
||||
mux.Use(httpmw.CORS([]string{"https://example.com"}))
|
||||
```
|
||||
|
||||
Pass `[]string{"*"}` to `CORS` to allow any origin (development only).
|
||||
|
||||
## Middleware
|
||||
|
||||
### Recover
|
||||
|
||||
```go
|
||||
mux.Use(httpmw.Recover())
|
||||
```
|
||||
|
||||
Wraps the handler in a `defer/recover`. On panic, writes `500 Internal Server Error` and captures `debug.Stack()`. No logger is required.
|
||||
|
||||
### RequestID
|
||||
|
||||
```go
|
||||
mux.Use(httpmw.RequestID(uuid.NewString))
|
||||
```
|
||||
|
||||
Calls `generator()` on every request, stores the ID with `logz.WithRequestID`, and writes it to the `X-Request-ID` response header. Must run before `RequestLogger` so the ID is in context when the logger reads it.
|
||||
|
||||
### RequestLogger
|
||||
|
||||
```go
|
||||
mux.Use(httpmw.RequestLogger(logger))
|
||||
```
|
||||
|
||||
Logs `method`, `path`, `status`, `latency`, and `request_id` after the inner handler returns. Uses `logger.Error` for 5xx responses and `logger.Info` for all others.
|
||||
|
||||
### CORS
|
||||
|
||||
```go
|
||||
mux.Use(httpmw.CORS([]string{"https://app.example.com", "https://admin.example.com"}))
|
||||
```
|
||||
|
||||
Sets `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, and `Access-Control-Allow-Headers` for matching origins. OPTIONS requests are short-circuited with 204 No Content.
|
||||
|
||||
Allowed methods: `GET, HEAD, PUT, PATCH, POST, DELETE, OPTIONS`
|
||||
Allowed headers: `Content-Type, Authorization, X-Request-ID`
|
||||
|
||||
## Logger interface
|
||||
|
||||
`RequestLogger` accepts any value satisfying the `Logger` interface — satisfied by `logz.Logger` via duck typing:
|
||||
|
||||
```go
|
||||
type Logger interface {
|
||||
Info(msg string, args ...any)
|
||||
Error(msg string, err error, args ...any)
|
||||
With(args ...any) Logger
|
||||
}
|
||||
```
|
||||
|
||||
## StatusRecorder
|
||||
|
||||
`StatusRecorder` is exported for use in custom middleware that needs to inspect the written status code:
|
||||
|
||||
```go
|
||||
rec := &httpmw.StatusRecorder{ResponseWriter: w, Status: http.StatusOK}
|
||||
next.ServeHTTP(rec, r)
|
||||
fmt.Println(rec.Status) // e.g. 404
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `code.nochebuena.dev/go/logz`
|
||||
4
go.mod
4
go.mod
@@ -1,5 +1,5 @@
|
||||
module code.nochebuena.dev/go/httpmw
|
||||
|
||||
go 1.25
|
||||
go 1.26
|
||||
|
||||
require code.nochebuena.dev/go/logz v0.9.0
|
||||
require code.nochebuena.dev/go/logz v1.0.1
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1,2 +1,2 @@
|
||||
code.nochebuena.dev/go/logz v0.9.0 h1:wfV7vtI4V/8ED7Hm31Fbql7Y5iOGrlHN4X8Z5ajTZZE=
|
||||
code.nochebuena.dev/go/logz v0.9.0/go.mod h1:qODhSbKb+tWE7rdhHLcKweiP5CgwIaWoZxadCT3bQV8=
|
||||
code.nochebuena.dev/go/logz v1.0.1 h1:kK9aZo19L208CwCr2D/dbSOMaOv62cXsigMSsdFu+8Y=
|
||||
code.nochebuena.dev/go/logz v1.0.1/go.mod h1:YNpNm03fURm2v0ySh/477z9AJhtfRcd9rFOW6fFqgNM=
|
||||
|
||||
Reference in New Issue
Block a user