Skip to content

Validation

The validation package wraps go-playground/validator and produces user-friendly field error messages.

import "github.com/slice-soft/ss-keel-core/validation"
func Validate(s any) []FieldError

Validates a struct using validate struct tags. Returns nil if the struct is valid.

type CreateUserDTO struct {
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"min=18"`
}
dto := CreateUserDTO{Name: "", Email: "invalid", Age: 15}
errors := validation.Validate(&dto)
// errors:
// [
// { "field": "name", "message": "this field is required" },
// { "field": "email", "message": "must be a valid email" },
// { "field": "age", "message": "minimum 18 characters" },
// ]
type FieldError struct {
Field string `json:"field"`
Message string `json:"message"`
}
TagMessage
requiredthis field is required
emailmust be a valid email
min=Nminimum N characters
max=Nmaximum N characters
uuidmust be a valid UUID
uuid4must be a valid UUID
numericmust be a numeric value
urlmust be a valid URL

For any unrecognized tag, the raw validator message is used as a fallback.

Ctx.ParseBody calls Validate internally. You don’t need to call it manually when using ParseBody:

func (c *UserController) create(ctx *core.Ctx) error {
var dto CreateUserDTO
if err := ctx.ParseBody(&dto); err != nil {
// 400 Bad Request — malformed JSON
// 422 Unprocessable Entity — validation errors
return err
}
// dto is valid here
}

The 422 response body:

{
"errors": [
{ "field": "email", "message": "must be a valid email" },
{ "field": "name", "message": "this field is required" }
]
}

You can call Validate independently of ParseBody:

type UpdateProfileDTO struct {
Bio string `validate:"max=500"`
URL string `validate:"omitempty,url"`
}
dto := UpdateProfileDTO{Bio: strings.Repeat("x", 600), URL: "not-a-url"}
if errs := validation.Validate(&dto); errs != nil {
// handle errs
}

For a full list of available validator tags, see the go-playground/validator documentation.