httpx is a small helper library to work with Go's standard net/http package. The package is designed to be simple yet powerful, providing just the essential features needed for building HTTP APIs without the complexity of larger frameworks.
The main asset of httpx is its error-returning handlers and middleware pattern. Instead of handling errors within each handler, errors are returned and managed centrally by a single errorHandler function. This dramatically reduces boilerplate error handling code in your handlers.
The router and handler groups can be used standalone with the standard http.ListenAndServe or with the provided httpx.ListenAndServe function which includes graceful shutdown support.
- Error-returning handlers/middlewares: returns errors instead of handling them inline
- Centralized error handling: All errors managed by a single errorHandler function
- Route grouping: Organize routes with prefixes and shared middleware
- Middleware chain: Composable middleware with Next() pattern
- Graceful shutdown: Optional ListenAndServe with built-in signal handling
import "https://github.com/quentinalbertone/httpx"
func main() {
router := httpx.NewRouter(nil, nil)
router.GET("/hello", func(w http.ResponseWriter, r *http.Request) error {
return httpx.JSON(w, http.StatusOK, map[string]string{"message": "Hello, World!"})
})
http.ListenAndServe(":8080", router)
}func AuthMiddleware(w http.ResponseWriter, r *http.Request) error {
// Check authentication
if !isAuthenticated(r) {
return httpx.JSON(w, http.StatusUnauthorized, map[string]string{"error": "unauthorized"})
}
return httpx.Next(w, r) // Continue to next handler
}
func main() {
router := httpx.NewRouter(nil, nil)
router.GET("/protected", AuthMiddleware, func(w http.ResponseWriter, r *http.Request) error {
return httpx.JSON(w, http.StatusOK, map[string]string{"data": "secret"})
})
}func main() {
router := httpx.NewRouter(nil, nil)
api := router.Group("/api")
{
v1 := api.Group("/v1")
v1.GET("/users", GetUsers)
v1.POST("/users", CreateUser)
v1.PUT("/users/{id}", UpdateUser)
}
}func MyErrorHandler(fn httpx.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
// Custom error handling logic
var status int
var msg string
switch {
case errors.Is(err, customerr.ValidationErr):
status = http.StatusBadRequest
msg = err.Error()
case errors.Is(err, customerr.NotFoundErr):
status = http.StatusNotFound
msg = err.Error()
case errors.Is(err, ierrors.NotImplementedErr):
status = http.StatusNotImplemented
msg = "not implemented"
case errors.Is(err, ierrors.UnauthorizedErr):
status = http.StatusUnauthorized
msg = "not authorized"
default:
status = http.StatusInternalServerError
err = errors.New("internal server error")
}
httpx.JSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
}
}
func main() {
router := httpx.NewRouter(nil, MyErrorHandler)
}func main() {
router := httpx.NewRouter(nil, nil)
router.GET("/api/data", GetDataHandler)
http.ListenAndServe(":8080", router)
}func main() {
router := httpx.NewRouter(nil, nil)
router.GET("/api/data", GetDataHandler)
serverConf := func(server *http.Server) {
server.Addr = ":8080"
server.ReadTimeout = 5 * time.Second
server.WriteTimeout = 10 * time.Second
}
ctx := context.Background()
if err := httpx.ListenAndServe(ctx, router, serverConf); err != nil {
log.Fatal(err)
}
}The package is designed to be minimal and focused: it's a small helper that makes working with Go's net/http package more convenient by centralizing error handling and providing useful routing utilities.