httpx

package module
v0.0.0-...-b52a20e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 9, 2026 License: MIT Imports: 27 Imported by: 0

README

httpx

A lightweight, type-safe Go web framework built on the standard library.

Features

  • Standard Library First — Built on net/http.ServeMux (Go 1.22+ pattern syntax), fully compatible with http.Handler
  • Type-Safe Endpoints — Generic route builders with func(ctx context.Context, req *Req) (*Resp, error) signature
  • Auto OpenAPI Generation — Generate OpenAPI 3.0 specs from Go struct tags, with built-in Swagger UI
  • Flexible Binding — Automatic binding from query, path, header, body (JSON/XML/YAML/Form/Proto)
  • Built-in Middleware — Logger, Recovery, CORS out of the box
  • Protobuf Code Generationprotoc-gen-go-httpx plugin for gRPC-Gateway style HTTP APIs

Installation

go get github.com/plsenp/httpx

Requires Go 1.24+.

Quick Start

package main

import (
    "net/http"

    "github.com/plsenp/httpx"
)

func main() {
    srv := httpx.NewServer()
    r := srv.Route()

    r.GET("/hello", func(ctx *httpx.Ctx) error {
        return ctx.String(http.StatusOK, "Hello, World!")
    })

    srv.Start()
}

Routing

Basic Routes
r := srv.Route()

r.GET("/users", listUsers)
r.POST("/users", createUser)
r.PUT("/users/{id}", updateUser)
r.DELETE("/users/{id}", deleteUser)
Route Groups
api := r.Group("/api")
api.GET("/users", listUsers)

v1 := api.Group("/v1")
v1.GET("/status", getStatus)
Mount http.Handler
r.Mount("/assets", fileServer)

Type-Safe Endpoints

Use generic builders for compile-time type safety and automatic OpenAPI documentation:

type CreateUserReq struct {
    Name  string `json:"name" validate:"required"`
    Email string `json:"email" validate:"required,email"`
}

type UserResp struct {
    ID    int64  `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

httpx.POST(r, "/users", func(ctx context.Context, req *CreateUserReq) (*UserResp, error) {
    return &UserResp{ID: 1, Name: req.Name, Email: req.Email}, nil
}).
    Summary("Create a user").
    Description("Creates a new user with the given name and email").
    Tags("users").
    Register()

The builder automatically:

  • Binds request parameters to Req
  • Validates using go-playground/validator tags
  • Renders response as JSON (or other content types via Accept header)
  • Registers OpenAPI documentation
Supported Methods

httpx.GET, httpx.POST, httpx.PUT, httpx.DELETE, httpx.PATCH, httpx.HEAD, httpx.OPTIONS

Request Binding

Automatic Binding

ctx.Bind(&v) automatically selects the binding source based on HTTP method:

Method Binding Sources
GET, HEAD, OPTIONS, DELETE Query + Path
POST, PUT, PATCH Body + Path
Manual Binding
ctx.BindQuery(&v)     // Query parameters only
ctx.BindPath(&v)      // Path parameters only
ctx.BindHeader(&v)    // Headers only
ctx.BindBody(&v)      // Request body only
ctx.BindJSON(&v)      // JSON body only
ctx.BindForm(&v)      // URL-encoded form only
ctx.BindMultipart(&v) // Multipart form (including files)
Struct Tags
type SearchReq struct {
    ID     string `path:"id"`                          // Path parameter
    Q      string `query:"q"`                          // Query parameter
    Token  string `header:"X-Token"`                   // Header
    Name   string `json:"name"`                        // JSON body
    Avatar string `file:"avatar"`                      // Uploaded file (*multipart.FileHeader)
    Photos []string `file:"photos"`                    // Multiple files ([]*multipart.FileHeader)
}

Response Rendering

ctx.JSON(code, data)                  // JSON response
ctx.String(code, "Hello %s", name)    // Plain text
ctx.Data(code, "image/png", bytes)    // Raw bytes
ctx.Render(code, data)                // Auto-negotiate by Accept header

Validation

Uses go-playground/validator tags:

type Req struct {
    Name  string `json:"name" validate:"required"`
    Email string `json:"email" validate:"required,email"`
    Age   int    `json:"age" validate:"gte=0,lte=150"`
}

Call ctx.Validate(&req) or use type-safe endpoints (auto-validated).

Middleware

Built-in Middleware
import "github.com/plsenp/httpx/middleware"

srv := httpx.NewServer(
    httpx.WithMiddleware(
        middleware.Logger,
        middleware.Recovery,
        middleware.CORS(middleware.DefaultCORSConfig),
    ),
)
Custom Middleware

Middleware follows the standard func(http.Handler) http.Handler pattern:

func Auth(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token == "" {
            http.Error(w, "unauthorized", http.StatusUnauthorized)
            return
        }
        next.ServeHTTP(w, r)
    })
}
Route-Level Middleware
r.GET("/admin", handler, Auth)
r.Use(Auth) // applies to all routes in this group
CORS Configuration
corsConfig := middleware.CORSConfig{
    AllowOrigins:     []string{"https://example.com"},
    AllowMethods:     []string{"GET", "POST"},
    AllowHeaders:     []string{"Content-Type", "Authorization"},
    AllowCredentials: true,
    ExposeHeaders:    []string{"X-Custom-Header"},
    MaxAge:           3600,
}
middleware.CORS(corsConfig)

OpenAPI Documentation

Enable OpenAPI
import "github.com/plsenp/httpx/openapi"

srv := httpx.NewServer(httpx.WithOpenAPISpec(openapi.Config{
    Title:       "My API",
    Version:     "1.0.0",
    Description: "My awesome API",
}))
Endpoints
Path Description
/docs/openapi.json OpenAPI 3.0 JSON spec
/docs Swagger UI
Struct Tags for OpenAPI
type User struct {
    Name  string `json:"name" validate:"required" description:"User's full name" example:"John Doe"`
    Email string `json:"email" validate:"required,email" description:"Email address" example:"john@example.com"`
    Age   int    `json:"age" description:"Age in years" example:"30"`
}

Type-safe endpoint builders automatically register OpenAPI operations with summary, description, tags, and schema.

Error Handling

import httpxerrors "github.com/plsenp/httpx/errors"

// Error types
httpxerrors.NewBindError("message", err)       // 400
httpxerrors.NewValidateError("message", err)   // 400
httpxerrors.NewBusinessError(409, "conflict", err) // Custom code
httpxerrors.NewInternalError("message", err)   // 500

// Type checking
httpxerrors.IsBindError(err)
httpxerrors.IsValidateError(err)
httpxerrors.IsBusinessError(err)
httpxerrors.IsInternalError(err)

Server Configuration

srv := httpx.NewServer(
    httpx.WithAddr(":9090"),
    httpx.WithReadTimeout(30*time.Second),
    httpx.WithReadHeaderTimeout(10*time.Second),
    httpx.WithWriteTimeout(30*time.Second),
    httpx.WithIdleTimeout(60*time.Second),
    httpx.WithMiddleware(middleware.Logger, middleware.Recovery),
    httpx.WithOpenAPISpec(openapi.Config{
        Title:   "My API",
        Version: "1.0.0",
    }),
)

Protobuf Code Generation

Use protoc-gen-go-httpx to generate HTTP routes from protobuf definitions with google.api.http annotations:

protoc --go_out=. --go-httpx_out=. your.proto

Generated code provides:

type UserServiceHTTPServer interface {
    GetUser(context.Context, *GetUserReq) (*User, error)
    CreateUser(context.Context, *CreateUserReq) (*User, error)
}

func RegisterUserServiceRoutes(router *httpx.Router, service UserServiceHTTPServer)

Context API

Method Description
ctx.Bind(&v) Auto-bind request parameters
ctx.Validate(&v) Validate struct
ctx.JSON(code, v) JSON response
ctx.String(code, format, args...) Text response
ctx.Data(code, contentType, data) Raw data response
ctx.Render(code, v) Content-negotiated response
ctx.Param(key) Path parameter
ctx.Query(key) Query parameter
ctx.Header(key) Request header
ctx.Request() Underlying *http.Request
ctx.Writer() *ResponseWriter
ctx.RawWriter() Underlying http.ResponseWriter
ctx.Get(key) Get context value
ctx.Set(key, value) Set context value

Content Negotiation

The framework supports multiple content types via Content-Type and Accept headers:

  • application/json (default)
  • application/xml
  • application/yaml
  • application/x-www-form-urlencoded
  • multipart/form-data
  • application/protobuf

License

MIT

Documentation

Index

Constants

View Source
const DefaultMaxMemory = 32 << 20 // 32MB

DefaultMaxMemory is the default max memory for multipart form.

Variables

This section is empty.

Functions

func ContentSubtype

func ContentSubtype(contentType string) string

Types

type BindFunc

type BindFunc func(r *http.Request, v any) error

type Ctx

type Ctx struct {
	// contains filtered or unexported fields
}

Ctx encapsulates the HTTP request and response, providing convenient methods for binding, validation, and rendering. It is passed to all handlers and middleware in the framework.

func (*Ctx) Bind

func (c *Ctx) Bind(v any) error

Bind binds request parameters to the struct. It supports query, path, and body binding. If the request method is GET, HEAD, OPTIONS, or DELETE, it binds query and path parameters. If the request method is POST, PUT or PATCH, it binds body and path parameters.

func (*Ctx) BindBody

func (c *Ctx) BindBody(v any) error

BindBody only binds request body.

func (*Ctx) BindForm

func (c *Ctx) BindForm(v any) error

func (*Ctx) BindHeader

func (c *Ctx) BindHeader(v any) error

BindHeader only binds headers.

func (*Ctx) BindJSON

func (c *Ctx) BindJSON(v any) error

BindJSON binds JSON body to the struct.

func (*Ctx) BindMultipart

func (c *Ctx) BindMultipart(v any) error

func (*Ctx) BindPath

func (c *Ctx) BindPath(v any) error

BindPath only binds path parameters.

func (*Ctx) BindQuery

func (c *Ctx) BindQuery(v any) error

BindQuery only binds query parameters.

func (*Ctx) Data

func (c *Ctx) Data(code int, contentType string, data []byte) error

Data writes raw bytes response with custom content type.

func (*Ctx) Get

func (c *Ctx) Get(key any) any

func (*Ctx) Header

func (c *Ctx) Header(key string) string

Header gets header by key.

func (*Ctx) JSON

func (c *Ctx) JSON(code int, v any) error

JSON writes JSON response (alias for Render).

func (*Ctx) Param

func (c *Ctx) Param(key string) string

Param gets path parameter by key.

func (*Ctx) Query

func (c *Ctx) Query(key string) string

Query gets query parameter by key.

func (*Ctx) RawWriter

func (c *Ctx) RawWriter() http.ResponseWriter

RawWriter returns the underlying http.ResponseWriter.

func (*Ctx) Render

func (c *Ctx) Render(code int, v any) error

Render writes response using the configured renderer.

func (*Ctx) Request

func (c *Ctx) Request() *http.Request

Request returns the http.Request.

func (*Ctx) Set

func (c *Ctx) Set(key, value any)

func (*Ctx) String

func (c *Ctx) String(code int, format string, args ...any) error

String writes string response.

func (*Ctx) Validate

func (c *Ctx) Validate(v any) error

Validate validates the struct.

func (*Ctx) Writer

func (c *Ctx) Writer() *ResponseWriter

Writer returns the ResponseWriter.

type HandlerFunc

type HandlerFunc func(ctx *Ctx) error

HandlerFunc is the handler function signature

type Middleware

type Middleware func(http.Handler) http.Handler

type RenderFunc

type RenderFunc func(w http.ResponseWriter, r *http.Request, status int, v any) error

type ResponseWriter

type ResponseWriter struct {
	http.ResponseWriter
	// contains filtered or unexported fields
}

func NewResponseWriter

func NewResponseWriter(w http.ResponseWriter) *ResponseWriter

func (*ResponseWriter) Flush

func (w *ResponseWriter) Flush()

func (*ResponseWriter) Hijack

func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error)

func (*ResponseWriter) Push

func (w *ResponseWriter) Push(target string, opts *http.PushOptions) error

func (*ResponseWriter) Size

func (w *ResponseWriter) Size() int

func (*ResponseWriter) Status

func (w *ResponseWriter) Status() int

func (*ResponseWriter) Unwrap

func (w *ResponseWriter) Unwrap() http.ResponseWriter

func (*ResponseWriter) Write

func (w *ResponseWriter) Write(data []byte) (int, error)

func (*ResponseWriter) WriteHeader

func (w *ResponseWriter) WriteHeader(code int)

func (*ResponseWriter) Written

func (w *ResponseWriter) Written() bool

type RouteBuilder

type RouteBuilder[Req any, Resp any] struct {
	// contains filtered or unexported fields
}

RouteBuilder is a generic route builder supporting method chaining

func DELETE

func DELETE[Req any, Resp any](
	router *Router,
	path string,
	handler func(ctx context.Context, req *Req) (*Resp, error),
) *RouteBuilder[Req, Resp]

DELETE creates a DELETE route builder (not auto-registered, call Register needed)

func GET

func GET[Req any, Resp any](
	router *Router,
	path string,
	handler func(ctx context.Context, req *Req) (*Resp, error),
) *RouteBuilder[Req, Resp]

GET creates a GET route builder (not auto-registered, call Register needed)

func HEAD[Req any, Resp any](
	router *Router,
	path string,
	handler func(ctx context.Context, req *Req) (*Resp, error),
) *RouteBuilder[Req, Resp]

HEAD creates a HEAD route builder (not auto-registered, call Register needed)

func OPTIONS

func OPTIONS[Req any, Resp any](
	router *Router,
	path string,
	handler func(ctx context.Context, req *Req) (*Resp, error),
) *RouteBuilder[Req, Resp]

OPTIONS creates an OPTIONS route builder (not auto-registered, call Register needed)

func PATCH

func PATCH[Req any, Resp any](
	router *Router,
	path string,
	handler func(ctx context.Context, req *Req) (*Resp, error),
) *RouteBuilder[Req, Resp]

PATCH creates a PATCH route builder (not auto-registered, call Register needed)

func POST

func POST[Req any, Resp any](
	router *Router,
	path string,
	handler func(ctx context.Context, req *Req) (*Resp, error),
) *RouteBuilder[Req, Resp]

POST creates a POST route builder (not auto-registered, call Register needed)

func PUT

func PUT[Req any, Resp any](
	router *Router,
	path string,
	handler func(ctx context.Context, req *Req) (*Resp, error),
) *RouteBuilder[Req, Resp]

PUT creates a PUT route builder (not auto-registered, call Register needed)

func (*RouteBuilder[Req, Resp]) Description

func (b *RouteBuilder[Req, Resp]) Description(description string) *RouteBuilder[Req, Resp]

Description sets the route description, returns self for chaining

func (*RouteBuilder[Req, Resp]) Middlewares

func (b *RouteBuilder[Req, Resp]) Middlewares(mws ...Middleware) *RouteBuilder[Req, Resp]

Middlewares sets the route middlewares, returns self for chaining

func (*RouteBuilder[Req, Resp]) Register

func (b *RouteBuilder[Req, Resp]) Register()

Register registers the route to the router (end of chain)

func (*RouteBuilder[Req, Resp]) Summary

func (b *RouteBuilder[Req, Resp]) Summary(summary string) *RouteBuilder[Req, Resp]

Summary sets the route summary, returns self for chaining

func (*RouteBuilder[Req, Resp]) Tags

func (b *RouteBuilder[Req, Resp]) Tags(tags ...string) *RouteBuilder[Req, Resp]

Tags sets the route tags, returns self for chaining

type Router

type Router struct {
	// contains filtered or unexported fields
}

func (*Router) CONNECT

func (r *Router) CONNECT(relativePath string, handler HandlerFunc, middleware ...Middleware)

func (*Router) DELETE

func (r *Router) DELETE(relativePath string, handler HandlerFunc, middleware ...Middleware)

func (*Router) GET

func (r *Router) GET(relativePath string, handler HandlerFunc, middleware ...Middleware)

func (*Router) Group

func (r *Router) Group(prefix string, middleware ...Middleware) *Router

func (*Router) HEAD

func (r *Router) HEAD(relativePath string, handler HandlerFunc, middleware ...Middleware)

func (*Router) Mount

func (r *Router) Mount(prefix string, handler http.Handler, middleware ...Middleware)

func (*Router) OPTIONS

func (r *Router) OPTIONS(relativePath string, handler HandlerFunc, middleware ...Middleware)

func (*Router) PATCH

func (r *Router) PATCH(relativePath string, handler HandlerFunc, middleware ...Middleware)

func (*Router) POST

func (r *Router) POST(relativePath string, handler HandlerFunc, middleware ...Middleware)

func (*Router) PUT

func (r *Router) PUT(relativePath string, handler HandlerFunc, middleware ...Middleware)

func (*Router) TRACE

func (r *Router) TRACE(relativePath string, handler HandlerFunc, middleware ...Middleware)

func (*Router) Use

func (r *Router) Use(middleware ...Middleware)

type Server

type Server struct {
	// contains filtered or unexported fields
}

func NewServer

func NewServer(opts ...ServerOption) *Server

func (*Server) Route

func (s *Server) Route() *Router

func (*Server) Start

func (s *Server) Start() error

func (*Server) Stop

func (s *Server) Stop(ctx context.Context) error

type ServerOption

type ServerOption func(*Server)

func WithAddr

func WithAddr(addr string) ServerOption

func WithIdleTimeout

func WithIdleTimeout(timeout time.Duration) ServerOption

func WithMiddleware

func WithMiddleware(md ...Middleware) ServerOption

func WithOpenAPISpec

func WithOpenAPISpec(spec openapi.Config) ServerOption

func WithReadHeaderTimeout

func WithReadHeaderTimeout(timeout time.Duration) ServerOption

func WithReadTimeout

func WithReadTimeout(timeout time.Duration) ServerOption

func WithWriteTimeout

func WithWriteTimeout(timeout time.Duration) ServerOption

type Validator

type Validator interface {
	Validate(any) error
	Underlying() any
}

Directories

Path Synopsis
cmd
proto
Package proto defines the protobuf codec.
Package proto defines the protobuf codec.
xml
Package errors provides standardized error types for httpx
Package errors provides standardized error types for httpx

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL