Validation
go-playground/validator for all request/response validation
Section titled “go-playground/validator for all request/response validation”Decode wire bodies with the validation core helper —
validation.DecodeAndValidate(reader, &v) — which runs
encoding/json + validator/v10 in one step. Validator tags
declare the rules inline; no hand-rolled if chains in the handler.
package backup
// BackupRequest is the wire body of POST /api/method/shc.backup.create.type BackupRequest struct { StackName string `json:"stack_name" validate:"required,min=1,max=255,alphanumDash"` Environment string `json:"environment,omitempty"` IncludeVault bool `json:"include_vault,omitempty"`}Register custom validators in core/validation/validation.go (ce module)
once at process start so every module sees them:
validation.Register("alphanumDash", func(fl validator.FieldLevel) bool { v := fl.Field().String() for _, r := range v { switch { case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r >= '0' && r <= '9', r == '-' || r == '_': default: return false } } return true})Variable types
Section titled “Variable types”SHC supports typed variables with automatic validation:
| Type | Validation |
|---|---|
string | Any string (default) |
integer | Numeric only |
boolean | true/false/yes/no |
password | Non-empty, masked in output |
secret | Like password, stored in vault |
port | 1-65535 |
url | Valid URL format |
email | Valid email format |
path | Valid filesystem path |
json | Valid JSON |
Custom regex validation
Section titled “Custom regex validation”# vars.yaml with custom regexvars: username@email: User email custom_id@'^[a-z][a-z0-9-]{2,30}$': Custom identifierValidation layers
Section titled “Validation layers”- Wire decode + struct validate (
validation.DecodeAndValidate) — JSON shape +validatortag rules. - Service — business-logic invariants (duplicate stack name, tenant quota exceeded, …).
- Database — constraints (NOT NULL, UNIQUE, FK).
func (s *Service) CreateStack(ctx context.Context, req StackRequest) (*Stack, error) { // validation.DecodeAndValidate already ran in the router.
if exists, err := s.stackExists(ctx, req.Name); err != nil { return nil, err } else if exists { return nil, X409003STKEXS(req.Name, req.Environment) }
// Database enforces (tenant_name, name, environment) uniqueness. return s.create(ctx, req)}Error messages
Section titled “Error messages”Return clear, actionable validation errors via the coded factory:
// Good — names the field and the valuereturn nil, X400002VALINP(fmt.Sprintf("stack name must be alphanumeric, got %q", req.Name))
// Bad — opaquereturn nil, X400002VALINP("bad value")shc@docs:~$