Roamer is a flexible, extensible HTTP request parser for Go that makes handling and extracting data from HTTP requests effortless. It provides a declarative way to map HTTP request data to Go structs using struct tags.
graph TD
subgraph "Input"
A[HTTP Request]
end
subgraph "Data Sources"
B1[Headers]
B2[Cookies]
B3[Query Params]
B4[Path Variables]
B5[Request Body]
B6[Custom]
end
subgraph "Roamer Core Engine"
direction LR
P[Parsers]
D[Decoders]
F[Formatters]
end
subgraph "Output"
E[Populated Go Struct]
end
A --> B1 & B2 & B3 & B4 & B5 & B6
B1 & B2 & B3 & B4 & B6 -- values for --> P
B5 -- content for --> D
P -- parsed data --> F
D -- decoded data --> F
F -- formatted values --> E
classDef source stroke:#d4ac0d,stroke-width:4px
classDef core stroke:#0097c0,stroke-width:4px
classDef io stroke:#333,stroke-width:4px
class A,E io
class B1,B2,B3,B4,B5,B6 source
class P,D,F core
- Multiple data sources: Parse data from HTTP headers, cookies, query parameters, path variables, and request body
- Content-type based decoding: Automatically decode JSON, XML, form data, and multipart forms
- Default Values: Set default values for fields using the
default
tag - Formatters: Transform parsed data (trim strings, apply numeric constraints, handle time zones, manipulate slices)
- Router integration: Built-in support for Chi, Gorilla Mux, and HttpRouter
- Type conversion: Automatic conversion of string values to appropriate Go types
- Extensibility: Easily create custom parsers, decoders, and formatters
- Middleware support: Convenient middleware for integrating with HTTP handlers
- Body preservation: Read request body multiple times when needed
go get -u github.com/slipros/roamer@latest
For router integrations:
# Chi router
go get -u github.com/slipros/roamer/pkg/chi@latest
# Gorilla Mux
go get -u github.com/slipros/roamer/pkg/gorilla@latest
# HttpRouter
go get -u github.com/slipros/roamer/pkg/httprouter@latest
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/slipros/roamer"
"github.com/slipros/roamer/decoder"
"github.com/slipros/roamer/formatter"
"github.com/slipros/roamer/parser"
)
// Define request struct with tags
type CreateUserRequest struct {
Name string `json:"name" string:"trim_space"`
Email string `json:"email" string:"trim_space,lower"`
Age int `query:"age" numeric:"min=18"`
UserAgent string `header:"User-Agent"`
}
func main() {
// Initialize roamer
r := roamer.NewRoamer(
roamer.WithDecoders(decoder.NewJSON()),
roamer.WithParsers(
parser.NewHeader(),
parser.NewQuery(),
),
roamer.WithFormatters(
formatter.NewString(),
formatter.NewNumeric(),
),
)
// Create handler
http.HandleFunc("/users", func(w http.ResponseWriter, req *http.Request) {
var userReq CreateUserRequest
// Parse request
if err := r.Parse(req, &userReq); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Use parsed data
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(map[string]any{
"name": userReq.Name,
"email": userReq.Email,
"age": userReq.Age,
"user_agent": userReq.UserAgent,
}); err != nil {
log.Printf("Failed to encode response: %v", err)
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
return
}
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
Comprehensive examples are available in the examples/ directory:
- basic/cmd/simple - Simple request parsing
- basic/cmd/middleware - Using roamer as middleware
- chi_router/ - Chi router integration with path parameters
- gorilla_router/ - Gorilla Mux integration
- httprouter/ - HttpRouter integration
- formatters/ - String, numeric, time, and slice formatters
- multipart/ - File upload handling
- custom_parser/ - Custom parser for extracting data from request context
- custom_decoder/ - Custom YAML decoder implementation
- custom_formatter/ - Custom phone number formatter
- body_preservation/ - Reading request body multiple times
See the examples README for a complete list and how to run them.
Tag | Description | Example |
---|---|---|
json |
Parse from JSON body | json:"name" |
xml |
Parse from XML body | xml:"name" |
form |
Parse from URL-encoded form | form:"name" |
multipart |
Parse from multipart form | multipart:"file" |
query |
Parse from query parameters | query:"page" |
header |
Parse from HTTP headers | header:"User-Agent" |
cookie |
Parse from cookies | cookie:"session_id" |
path |
Parse from path variables | path:"id" |
default |
Default value if not present | default:"1" |
Tag | Operations | Example |
---|---|---|
string |
trim_space , lower , upper , title , snake , camel , kebab , slug |
string:"trim_space,lower" |
numeric |
min=N , max=N , abs , round , ceil , floor |
numeric:"min=0,max=100" |
time |
timezone=TZ , truncate=UNIT , start_of_day , end_of_day |
time:"timezone=UTC" |
slice |
unique , sort , sort_desc , compact , limit=N |
slice:"unique,sort" |
type CustomParser struct{}
func (p *CustomParser) Parse(r *http.Request, tag reflect.StructTag, _ parser.Cache) (any, bool) {
tagValue, ok := tag.Lookup("custom")
if !ok {
return "", false
}
// Extract and return value
return value, true
}
func (p *CustomParser) Tag() string {
return "custom"
}
type CustomDecoder struct{}
func (d *CustomDecoder) Decode(r *http.Request, ptr any) error {
// Decode request body into ptr
return nil
}
func (d *CustomDecoder) ContentType() string {
return "application/custom"
}
type CustomFormatter struct{}
func (f *CustomFormatter) Format(tag reflect.StructTag, ptr any) error {
tagValue, ok := tag.Lookup("custom_format")
if !ok {
return nil
}
// Format the value pointed to by ptr
return nil
}
func (f *CustomFormatter) Tag() string {
return "custom_format"
}
See the examples/ directory for complete custom extension examples.
Roamer is designed for production use with:
- Efficient reflection techniques
- Caching for improved performance
- Optional
sync.Pool
support for high-throughput applications - Minimal allocations in hot paths
- Separate request and response structs - Use dedicated structs for parsing requests
- Endpoint-specific structs - Create tailored structs for each endpoint to minimize overhead
- Use formatters - Let roamer handle common transformations (trimming, case conversion, etc.)
- Combine with validation - Use roamer for parsing, then validate with libraries like validator.go
Contributions are welcome! Please submit issues or pull requests.
Roamer is licensed under the MIT License. See the LICENSE file for details.