Getting Started
Getting Started with Roamer
This guide will help you get up and running with Roamer quickly.
Installation
Install Roamer using Go modules:
go get -u github.com/slipros/roamer@latest
For router integrations, install the specific packages you need:
# Chi router
go get -u github.com/slipros/roamer/pkg/chi@latest
# Gorilla Mux router
go get -u github.com/slipros/roamer/pkg/gorilla@latest
# HttpRouter
go get -u github.com/slipros/roamer/pkg/httprouter@latest
Basic Concepts
Roamer works with three main components:
- Parsers - Extract data from different parts of HTTP requests (headers, query params, cookies, path)
- Decoders - Handle request body decoding based on Content-Type
- Formatters - Post-process parsed values (trim strings, format numbers, etc.)
Your First Roamer Application
Let’s create a simple API endpoint that handles user creation:
Step 1: Define Your Request Structure
type CreateUserRequest struct {
// From JSON body
Name string `json:"name" string:"trim_space"`
Email string `json:"email" string:"trim_space,lower"`
Age int `json:"age" numeric:"min=0,max=120"`
// From query parameters
Source string `query:"source" default:"web"`
// From headers
UserAgent string `header:"User-Agent"`
ContentType string `header:"Content-Type"`
}
Step 2: Initialize Roamer
r := roamer.NewRoamer(
roamer.WithDecoders(decoder.NewJSON()),
roamer.WithParsers(
parser.NewHeader(),
parser.NewQuery(),
),
roamer.WithFormatters(
formatter.NewString(),
formatter.NewNumeric(),
),
)
Step 3: Create Your Handler
func handleCreateUser(w http.ResponseWriter, req *http.Request) {
var userReq CreateUserRequest
// Parse the request
if err := r.Parse(req, &userReq); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Process the request
fmt.Printf("Creating user: %+v\n", userReq)
// Send response
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(map[string]any{
"status": "created",
"name": userReq.Name,
"email": userReq.Email,
"source": userReq.Source,
}); err != nil {
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
return
}
}
Step 4: Complete Example
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/slipros/roamer"
"github.com/slipros/roamer/decoder"
"github.com/slipros/roamer/formatter"
"github.com/slipros/roamer/parser"
)
type CreateUserRequest struct {
Name string `json:"name" string:"trim_space"`
Email string `json:"email" string:"trim_space,lower"`
Age int `json:"age" numeric:"min=0,max=120"`
Source string `query:"source" default:"web"`
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
if err := r.Parse(req, &userReq); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
fmt.Printf("Creating user: %+v\n", userReq)
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(map[string]any{
"status": "created",
"name": userReq.Name,
"email": userReq.Email,
"source": userReq.Source,
}); err != nil {
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
return
}
})
log.Println("Server starting on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("Server failed to start: %v", err)
}
}
Testing Your Application
Test your endpoint with curl:
curl -X POST http://localhost:8080/users?source=mobile \
-H "Content-Type: application/json" \
-H "User-Agent: MyApp/1.0" \
-d '{
"name": " John Doe ",
"email": "JOHN@EXAMPLE.COM",
"age": 30
}'
The parsed data will have:
Name
: “John Doe” (spaces trimmed)Email
: “john@example.com” (trimmed and lowercased)Age
: 30 (validated between 0-120)Source
: “mobile” (from query param)UserAgent
: “MyApp/1.0” (from header)
Using Middleware
For cleaner code, you can use Roamer’s middleware:
// Initialize roamer
r := roamer.NewRoamer(/* ... */)
// Use middleware
http.Handle("/users", roamer.Middleware[CreateUserRequest](r)(http.HandlerFunc(handleCreateUser)))
func handleCreateUser(w http.ResponseWriter, req *http.Request) {
var userReq CreateUserRequest
// Get parsed data from context
if err := roamer.ParsedDataFromContext(req.Context(), &userReq); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Process the request...
}
Common Struct Tags
Here are the most commonly used struct tags in Roamer:
Tag | Purpose | Example |
---|---|---|
json:"field" |
Parse from JSON body | json:"name" |
query:"param" |
Parse from query parameter | query:"page" |
header:"Header-Name" |
Parse from HTTP header | header:"User-Agent" |
cookie:"name" |
Parse from cookie | cookie:"session_id" |
path:"param" |
Parse from path variable | path:"id" |
default:"value" |
Default value if not found | default:"1" |
string:"operation" |
String formatting | string:"trim_space,lower" |
numeric:"constraint" |
Numeric constraints | numeric:"min=0,max=100" |
Next Steps
Now that you have Roamer working, explore more advanced features:
- Examples - Router integration, different content types, and complex use cases
- API Reference - Complete documentation of all parsers, decoders, and formatters
- Extending Roamer - Create custom components for your specific needs
Troubleshooting
Common Issues
Parsing fails silently
- Make sure your struct tags match the expected format
- Check that you’ve registered the appropriate parsers and decoders
Type conversion errors
- Verify the data format matches the Go type
- Use string formatters to clean data before type conversion
Missing values
- Check tag names match request parameter names exactly
- Use default values for optional fields
Performance issues
- Use request-specific structs instead of large generic ones
- Consider caching roamer instances for reuse
Body cannot be read multiple times
- Enable
WithPreserveBody()
option if you need multiple reads - Be aware of memory implications for large request bodies