prize is a Go package that provides a type-safe, generic HTTP handler with built-in middleware support. Built on top of github.com/4thPlanet/dispatch, it wires together session management, request logging, error handling, and content encoding into a single composable handler — so you can focus on your application logic rather than plumbing.
- Generic, type-safe request data — bind strongly-typed request context (
T) to each incoming request via a user-supplied factory function - Typed Handlers — Generic
TypedHandler[S, T]for type-safe request handling with session and custom data support - Session Management — pluggable
SessionStorewith a configurable initializer; defaults to an in-memory session store - Request Logging — Apache-style access logging with configurable format directives
- Error Handling — centralized, swappable error handler with panic recovery and stack trace logging
- Content Encoding — support for gzip, deflate, and custom encoders with Content-Type negotiation
- Middleware chaining — attach custom middleware that runs after the built-in layers
- WebSocket support — indirect, via the
gobwas/wsdependency pulled in throughdispatch
Go 1.26.1 or later
go get github.com/4thPlanet/prizepackage main
import (
"fmt"
"net/http"
"github.com/4thPlanet/prize"
"github.com/4thPlanet/prize/middleware"
)
// Define your session type. Must implement middleware.Session interface (Id() string, Load(*http.Request), Save(*http.Request))
type MySession struct {
UserID string
}
func (s MySession) Id() string { return s.UserID }
// Define your per-request data type.
type RequestData struct {
Name string
}
func main() {
// Create a typed handler.
// The first argument is a session initializer; the second extracts typed data from the request.
handler := prize.NewTypedHandler[MySession](
func() MySession { return MySession{} },
func(r *http.Request) RequestData {
return RequestData{Name: r.URL.Query().Get("name")}
},
)
// Register routes - HandlerData gives access to Session and RequestData
handler.HandleFunc("GET /hello", func(w http.ResponseWriter, hd *prize.HandlerData[MySession, RequestData]) {
fmt.Fprintf(w, "Hello, %s!", hd.Data.Name)
})
// Register custom middleware (logging, error handling, sessions, and content encoding
// are automatically prepended).
handler.UseMiddleware(/* your custom middleware here */)
http.ListenAndServe(":8080", handler)
}A more detailed example — including session usage, content negotiation, and authentication — is available in examples/basic.
The central type. S must satisfy middleware.Session; T is your arbitrary per-request data struct.
type TypedHandler[S middleware.Session, T any] struct { ... }func NewTypedHandler[S middleware.Session, T any](
sessionInit func() S,
fn func(*http.Request) T,
) *TypedHandler[S, T]Creates a new handler. sessionInit produces a blank session value; fn builds the typed request data from the incoming *http.Request.
func (mux *TypedHandler[S, T]) UseLog(format string, l io.Writer)Override the log format string and output destination. The default format is Apache Combined Log Format (%h %l %u %t "%r" %s %b); the default writer is log.Default().Writer().
func (mux *TypedHandler[S, T]) UseErrorHandler(
handler middleware.ErrorHandler[*HandlerData[S, T]],
ctn *dispatch.ContentTypeNegotiator,
)Swap in a custom error handler and content-type negotiator.
func (mux *TypedHandler[S, T]) UseContentEncoders(encs ...middleware.ContentEncoder)Register one or more response content encoders (e.g. gzip).
func (mux *TypedHandler[S, T]) UseSessionStore(store middleware.SessionStore[S], init func() S)Replace the default in-memory session store.
func (mux *TypedHandler[S, T]) UseMiddleware(mws ...dispatch.Middleware[*HandlerData[S, T]])Attach custom middleware. The following built-in middleware layers are automatically prepended in order:
- Logger — logs each request
- Error handler — catches and formats errors
- Session — loads/saves the session for each request
- Content encoding — applies response encoders
The context object passed to every route handler. Provides access to the raw request, the typed data, and the loaded session.
type HandlerData[S middleware.Session, T any] struct {
Data T
Session S
// contains unexported fields
}
func (d *HandlerData[S, T]) Request() *http.Request
func (d *HandlerData[S, T]) LoadSession(session S)Apache-style access logging with support for standard directives:
%h- Remote host%l- Remote logname%u- Remote user%t- Time%r- Request line%s- Status code%b- Response size%D- Request duration (microseconds)%T- Request duration (seconds)%{format}t- Custom time format%{Header}i/%{Header}o- Request/response headers%m- Request method%U- Request URL path%q- Query string%p- Local port%H- Request protocol- And more...
Catches panics and returns configurable error responses with stack trace logging. Supports content type negotiation for error responses.
Generic session management with:
Sessioninterface for custom session types (requiresId() string)DefaultSessionStorefor in-memory session storage- Cookie-based session tracking
- Configurable session lifecycle
Content encoding support:
gzipanddeflatecompression- Custom encoder support via
ContentEncoderinterface - Automatic
Accept-Encodingnegotiation
go run examples/basic/main.goThe example server starts on localhost:8080 with:
GET /- List users (supports HTML, JSON, CSV content negotiation)POST /- Add a new user (requires Basic Authentication)GET /panic- Test panic recovery middleware
# Build
go build ./...
# Run tests
go test ./...
# Run tests with verbose output
go test -v ./...| Module | Purpose |
|---|---|
github.com/4thPlanet/dispatch |
Typed HTTP routing and middleware chaining |
github.com/gobwas/ws |
WebSocket support (indirect) |
golang.org/x/sys |
System-level support (indirect) |
BSD 3-Clause License. See LICENSE for details.