Skip to content

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.