Options
6 minute read
Comprehensive reference for all configuration options available in the binding package.
Option Type
type Option func(*Config)
Options configure binding behavior. They can be passed to:
- Package-level functions (e.g.,
binding.JSON[T](data, opts...)) Binderconstructor (e.g.,binding.MustNew(opts...))Bindermethods (e.g.,binder.JSON[T](data, opts...))
Security Limits
WithMaxDepth
func WithMaxDepth(depth int) Option
Sets maximum struct nesting depth to prevent stack overflow from deeply nested structures.
Default: 32
Example:
user, err := binding.JSON[User](data, binding.WithMaxDepth(16))
Use Cases:
- Protect against malicious deeply nested JSON
- Limit resource usage
- Prevent stack overflow
WithMaxSliceLen
func WithMaxSliceLen(length int) Option
Sets maximum slice length to prevent memory exhaustion from large arrays.
Default: 10,000
Example:
params, err := binding.Query[Params](values, binding.WithMaxSliceLen(1000))
Use Cases:
- Protect against memory attacks
- Limit array sizes
- Control memory allocation
WithMaxMapSize
func WithMaxMapSize(size int) Option
Sets maximum map size to prevent memory exhaustion from large objects.
Default: 1,000
Example:
config, err := binding.JSON[Config](data, binding.WithMaxMapSize(500))
Use Cases:
- Protect against memory attacks
- Limit object sizes
- Control memory allocation
Unknown Field Handling
WithStrictJSON
func WithStrictJSON() Option
Convenience function that sets WithUnknownFields(UnknownError). Fails binding if JSON contains fields not in the struct.
Example:
user, err := binding.JSON[User](data, binding.WithStrictJSON())
if err != nil {
var unknownErr *binding.UnknownFieldError
if errors.As(err, &unknownErr) {
log.Printf("Unknown fields: %v", unknownErr.Fields)
}
}
Use Cases:
- API versioning
- Catch typos in field names
- Enforce strict contracts
WithUnknownFields
func WithUnknownFields(mode UnknownMode) Option
// Modes
const (
UnknownIgnore UnknownMode = iota // Ignore unknown fields (default)
UnknownWarn // Log warnings
UnknownError // Return error
)
Controls how unknown fields are handled.
Example:
user, err := binding.JSON[User](data,
binding.WithUnknownFields(binding.UnknownWarn))
Modes:
UnknownIgnore: Silently ignore (default, most flexible)UnknownWarn: Log warnings (for debugging)UnknownError: Fail binding (strict contracts)
Slice Parsing
WithSliceMode
func WithSliceMode(mode SliceMode) Option
// Modes
const (
SliceRepeat SliceMode = iota // ?tags=a&tags=b (default)
SliceCSV // ?tags=a,b,c
)
Controls how slices are parsed from query/form values.
Example:
// URL: ?tags=go,rust,python
params, err := binding.Query[Params](values,
binding.WithSliceMode(binding.SliceCSV))
Modes:
SliceRepeat: Repeated parameters (default, standard HTTP)SliceCSV: Comma-separated values (more compact)
Error Handling
WithAllErrors
func WithAllErrors() Option
Collects all binding errors instead of failing on the first error.
Example:
user, err := binding.JSON[User](data, binding.WithAllErrors())
if err != nil {
var multi *binding.MultiError
if errors.As(err, &multi) {
for _, e := range multi.Errors {
log.Printf("Field %s: %v", e.Field, e.Err)
}
}
}
Use Cases:
- Show all validation errors to user
- Debugging
- Comprehensive error reporting
Type Conversion
WithConverter
func WithConverter[T any](fn func(string) (T, error)) Option
Registers a custom type converter for type T.
Example:
import "github.com/google/uuid"
binder := binding.MustNew(
binding.WithConverter[uuid.UUID](uuid.Parse),
)
type User struct {
ID uuid.UUID `query:"id"`
}
user, err := binder.Query[User](values)
Use Cases:
- Custom types (UUID, decimal, etc.)
- Domain-specific types
- Third-party types
WithTimeLayouts
func WithTimeLayouts(layouts ...string) Option
Sets custom time parsing layouts. Replaces default layouts.
Default Layouts: See binding.DefaultTimeLayouts
Example:
binder := binding.MustNew(
binding.WithTimeLayouts(
"2006-01-02", // Date only
"01/02/2006", // US format
"2006-01-02 15:04:05", // DateTime
),
)
Tip: Extend defaults instead of replacing:
binder := binding.MustNew(
binding.WithTimeLayouts(
append(binding.DefaultTimeLayouts, "01/02/2006", "02-Jan-2006")...,
),
)
Observability
WithEvents
func WithEvents(events Events) Option
type Events struct {
FieldBound func(name, tag string)
UnknownField func(name string)
Done func(stats Stats)
}
type Stats struct {
FieldsBound int
ErrorCount int
Duration time.Duration
}
Registers event handlers for observing binding operations.
Example:
binder := binding.MustNew(
binding.WithEvents(binding.Events{
FieldBound: func(name, tag string) {
metrics.Increment("binding.field.bound",
"field:"+name, "source:"+tag)
},
UnknownField: func(name string) {
log.Warn("Unknown field", "name", name)
},
Done: func(stats binding.Stats) {
metrics.Histogram("binding.duration",
stats.Duration.Milliseconds())
metrics.Gauge("binding.fields", stats.FieldsBound)
},
}),
)
Use Cases:
- Metrics collection
- Debugging
- Performance monitoring
- Audit logging
Multi-Source Options
WithMergeStrategy
func WithMergeStrategy(strategy MergeStrategy) Option
// Strategies
const (
MergeLastWins MergeStrategy = iota // Last source wins (default)
MergeFirstWins // First source wins
)
Controls precedence when binding from multiple sources.
Example:
// First source wins
req, err := binding.Bind[Request](
binding.WithMergeStrategy(binding.MergeFirstWins),
binding.FromHeader(r.Header), // Highest priority
binding.FromQuery(r.URL.Query()), // Lower priority
)
Strategies:
MergeLastWins: Last source overwrites (default)MergeFirstWins: First non-empty value wins
JSON-Specific Options
WithDisallowUnknownFields
func WithDisallowUnknownFields() Option
Equivalent to WithStrictJSON(). Provided for clarity when explicitly disallowing unknown fields.
Example:
user, err := binding.JSON[User](data,
binding.WithDisallowUnknownFields())
WithMaxBytes
func WithMaxBytes(bytes int64) Option
Limits the size of JSON/XML data to prevent memory exhaustion.
Example:
user, err := binding.JSON[User](data,
binding.WithMaxBytes(1024 * 1024)) // 1MB limit
Use Cases:
- Protect against large payloads
- API rate limiting
- Resource management
Custom Options
WithTagHandler
func WithTagHandler(tagName string, handler TagHandler) Option
type TagHandler interface {
Get(fieldName, tagValue string) (string, bool)
}
Registers a custom struct tag handler.
Example:
type EnvTagHandler struct {
prefix string
}
func (h *EnvTagHandler) Get(fieldName, tagValue string) (string, bool) {
envKey := h.prefix + tagValue
val, exists := os.LookupEnv(envKey)
return val, exists
}
binder := binding.MustNew(
binding.WithTagHandler("env", &EnvTagHandler{prefix: "APP_"}),
)
type Config struct {
APIKey string `env:"API_KEY"` // Looks up APP_API_KEY
}
Option Combinations
Production Configuration
var ProductionBinder = binding.MustNew(
// Security
binding.WithMaxDepth(16),
binding.WithMaxSliceLen(1000),
binding.WithMaxMapSize(500),
binding.WithMaxBytes(10 * 1024 * 1024), // 10MB
// Strict validation
binding.WithStrictJSON(),
// Custom types
binding.WithConverter[uuid.UUID](uuid.Parse),
binding.WithConverter[decimal.Decimal](decimal.NewFromString),
// Time formats
binding.WithTimeLayouts(append(
binding.DefaultTimeLayouts,
"2006-01-02",
"01/02/2006",
)...),
// Observability
binding.WithEvents(binding.Events{
FieldBound: logFieldBound,
UnknownField: logUnknownField,
Done: recordMetrics,
}),
)
Development Configuration
var DevBinder = binding.MustNew(
// Lenient limits
binding.WithMaxDepth(32),
binding.WithMaxSliceLen(10000),
// Warnings instead of errors
binding.WithUnknownFields(binding.UnknownWarn),
// Collect all errors for debugging
binding.WithAllErrors(),
// Verbose logging
binding.WithEvents(binding.Events{
FieldBound: func(name, tag string) {
log.Printf("[DEBUG] Bound %s from %s", name, tag)
},
UnknownField: func(name string) {
log.Printf("[WARN] Unknown field: %s", name)
},
Done: func(stats binding.Stats) {
log.Printf("[DEBUG] Binding: %d fields, %d errors, %v",
stats.FieldsBound, stats.ErrorCount, stats.Duration)
},
}),
)
Testing Configuration
var TestBinder = binding.MustNew(
// Strict validation
binding.WithStrictJSON(),
// Fail fast
// (don't use WithAllErrors in tests)
// Smaller limits for test data
binding.WithMaxDepth(8),
binding.WithMaxSliceLen(100),
)
Option Precedence
When options are provided to both MustNew() and individual functions:
- Function-level options override binder-level options
- Options are applied in order (last wins for same option)
Example:
binder := binding.MustNew(
binding.WithMaxDepth(32), // Binder default
)
// This call uses maxDepth=16 (overrides binder default)
user, err := binder.JSON[User](data,
binding.WithMaxDepth(16))
Best Practices
1. Use Binders for Shared Configuration
// Good - shared configuration
var AppBinder = binding.MustNew(
binding.WithConverter[uuid.UUID](uuid.Parse),
binding.WithMaxDepth(16),
)
func Handler1(r *http.Request) {
user, err := AppBinder.JSON[User](r.Body)
}
func Handler2(r *http.Request) {
params, err := AppBinder.Query[Params](r.URL.Query())
}
2. Set Security Limits
// Good - protect against attacks
user, err := binding.JSON[User](data,
binding.WithMaxDepth(16),
binding.WithMaxSliceLen(1000),
binding.WithMaxBytes(1024*1024),
)
3. Use Strict Mode for APIs
// Good - catch client errors early
user, err := binding.JSON[User](data, binding.WithStrictJSON())
4. Collect All Errors for Forms
// Good - show all validation errors to user
form, err := binding.Form[Form](r.PostForm, binding.WithAllErrors())
if err != nil {
var multi *binding.MultiError
if errors.As(err, &multi) {
// Show all errors to user
for _, e := range multi.Errors {
addError(e.Field, e.Err.Error())
}
}
}
See Also
- API Reference - Core functions and types
- Performance - Optimization guide
- Troubleshooting - Common issues
For usage examples, see the Binding Guide.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.