Pipes
A pipe gets a pointer to the bound input struct after binding. Parsing (ParseIntPipe and friends) is already done by the binder, so pipes are for validation and normalising.
// No dependency: input types check themselves.app, err := dygo.Create(ctx, AppModule, validate.Use(validate.Methods()))
type NewUser struct { Email string `json:"email"` Name string `json:"name"`}
func (u *NewUser) Validate(ctx context.Context) error { var v validate.Errors if !strings.Contains(u.Email, "@") { v.Add("body", "email", "must be a valid email") } return v.Err()}
type CreateIn struct { Body NewUser `body:"json"` // Validate is found on the Body field too}With struct tags (separate module github.com/dygoframework/dygo/validate/playground, wraps go-playground/validator):
type CreateIn struct { Body struct { Email string `json:"email" validate:"required,email"` } `body:"json"` Limit int `query:"limit" validate:"lte=100"`}
app, err := dygo.Create(ctx, AppModule, validate.Use(playground.New()))Either way a failure is one 400 listing every field, named as the client sent it:
{"statusCode":400,"error":"Bad Request","message":"validation failed", "details":[{"source":"body","name":"email","reason":"must be a valid email"}, {"source":"query","name":"limit","value":"500","reason":"must be at most 100"}]}validate.WithErrorShape(fn) changes details (for example to a list of message strings). validate.Responses(schema) is an interceptor that checks handler results and turns a bad one into a 500.