SkillAgentSearch skills...

Roamer

Fast and flexible HTTP request parser for Go

Install / Use

/learn @slipros/Roamer

README

roamer

Go Report Card Build Status Coverage Status Go Reference Go Version GitHub release Mentioned in Awesome Go

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

Features

  • 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

Installation

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

Quick Start

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))
}

Examples

Comprehensive examples are available in the examples/ directory:

Basic Usage

Router Integration

Advanced Features

See the examples README for a complete list and how to run them.

Struct Tags Reference

Data Source Tags

| 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" |

Formatter Tags

| 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" |

Creating Extensions

Custom Parser

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"
}

Custom Decoder

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"
}

Custom Formatter

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.

Performance

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

Best Practices

  1. Separate request and response structs - Use dedicated structs for parsing requests
  2. Endpoint-specific structs - Create tailored structs for each endpoint to minimize overhead
  3. Use formatters - Let roamer handle common transformations (trimming, case conversion, etc.)
  4. Combine with validation - Use roamer for parsing, then validate with libraries like validator.go

Documentation

Contributing

Contributions are welcome! Please submit issues or pull requests.

License

Roamer is licensed under the MIT License. See the LICENSE file for details.

View on GitHub
GitHub Stars6
CategoryDevelopment
Updated7d ago
Forks0

Languages

Go

Security Score

90/100

Audited on Mar 24, 2026

No findings