Go Validator v10

Validation? Easy Peasy!

Renaldi
Tunaiku Tech
4 min readOct 20, 2020

--

Validator V10

Have you ever wrote a long block of validation code? How is that feeling? Have you ever wished that someone in this world would do that for you? Or perhaps you are wishing for a function like this?

err := ValidateEverything(dto)
if err != nil {
return err
}

Actually, your wish is granted. Meet Validator v10, a package that would do all your validation need.

I will always choose a lazy person to do a difficult job because a lazy person will find an easy way to do it. — Bill Gates

Validator v10 is a package that will handle validations for structs and individual fields based on tags.

To use this package, first, you have to create its instance.

import "github.com/go-playground/validator/v10"...validate := validator.New()

Now let’s have some cases. Imagine that you are asked to validate this

  • Name = required, 4 characters minimum, 15 characters maximum
  • Email = required, must be a correct email format
  • Age = required, numeric, more than 17
  • Driver License Number = not a required field, but if any, must be numeric and 12 characters

Struct Validation

based on the case above, let’s create a struct like this:

type person struct {
Name string `validate:"required,min=4,max=15"`
Email string `validate:"required,email"`
Age int `validate:"required,numeric,min=18"`
DriverLicenseNumber string `validate:"omitempty,len=12,numeric"`
}

As you can see above, that there’s a struct tag called validate and there you define its validation rules. Now let’s see the example:

p := person{
Name: "Joe",
Email: "dummyemail",
Age: 0,
DriverLicenseNumber: "",
}
err := validate.Struct(p)
fmt.Println(err)

And the result is:

Key: 'person.Name' Error:Field validation for 'Name' failed on the 'min' tag
Key: 'person.Email' Error:Field validation for 'Email' failed on the 'email' tag
Key: 'person.Age' Error:Field validation for 'Age' failed on the 'required' tag

Var Validation

Not only a struct validation. This validator v10 also provides validation for a single variable. For a single variable, you need validate.Var function.

name:="re"
err := validate.Var(name,"required,min=3")
fmt.Println(err)

And the result will be like this:

Key: '' Error:Field validation for '' failed on the 'min' tag

Translate the Error Message

Pretty easy huh? But there’s one more problem. As you can see from above that the error message is kinda less human-readable right? But don’t worry you can translate the error message. To do that, you need something called universal-translator.

validate := validator.New()english := en.New()
uni := ut.New(english, english)
trans, _ := uni.GetTranslator("en")
_ = enTranslations.RegisterDefaultTranslations(validate, trans)

Now we have registered our universal translator to our validator client. The last thing to do is create a function that would translate our validator’s error message.

func translateError(err error, trans ut.Translator) (errs []error) {
if err == nil {
return nil
}
validatorErrs := err.(validator.ValidationErrors)
for _, e := range validatorErrs {
translatedErr := fmt.Errorf(e.Translate(trans))
errs = append(errs, translatedErr)
}
return errs
}

Now the code will be like this:

p := person{
Name: "Joe",
Email: "dummyemail",
Age: 0,
DriverLicenseNumber: "",
}
err := validate.Struct(p)
errs := translateError(err, trans)
fmt.Println(errs)

And the result will be like this:

[Name must be at least 4 characters in length 
Email must be a valid email address
Age is a required field]

Add Your Own Tag

Validator v10 already provides a lot of tags for you to use. But in case it didn’t cover all of your cases, validator v10 provides a function for you to add your own tag. That’s right, your own rule. Pretty cool isn’t it?

First, create your own validation function first.

func validateNationalId(fl validator.FieldLevel) bool {
nationalId := fl.Field().String()

if len(nationalId) != lengthNationalId {
return false
}

regex, _ := regexp.Compile("\\D")
result := regex.MatchString(nationalId)
return !result
}

Second, Add your own validation function to the validator.

validate.RegisterValidation("national_id", validateNationalId)

Voilà, your own validation is ready to use.

Add Your Own Error Message

Not only your own validation function but also your own error message. So error from validator v10 will be translated into yours. Now let’s create a function to do that.

func addTranslation(tag string, errMessage string) {
registerFn := func(ut ut.Translator) error {
return ut.Add(tag, errMessage, false)
}

transFn := func(ut ut.Translator, fe validator.FieldError) string {
param := fe.Param()
tag := fe.Tag()

t, err := ut.T(tag, fe.Field(), param)
if err != nil {
return fe.(error).Error()
}
return t
}

_ = validate.RegisterTranslation(tag, trans, registerFn, transFn)
}

Here’s the step by step. First, you have to create the error message format like this:

errStartsWith string = "{0} must be starts with '{1}'"
//{0} should be the field, and {1} should be the param

Second, you have to register your message to our universal-translator.

registerFn := func(ut ut.Translator) error {
return ut.Add(tag, errMessage, false)
}

Third, Now you have to define how your tag will be translated.

transFn := func(ut ut.Translator, fe validator.FieldError) string {
param := fe.Param()
tag := fe.Tag()

t, err := ut.T(tag, fe.Field(), param) // tag, {0}, {1}, ..
if err != nil {
return fe.(error).Error()
}
return t
}

Then finally, use all of them to the built-in function of validator v10.

validate.RegisterTranslation(tag, trans, registerFn, transFn)

Then your call should be like this:

addTranslation("starts_with", errStartsWith)
// {0} must be starts with '{1}'

There’s no satisfaction without a struggle first. — Marty Liquori

Conclusion

Validator v10 has a lot of tags that you can use, It also provides a function to register your own tag and your own error translation. But unfortunately, the error translation is a little bit complicated but once you’re done, you don’t need to write any validation code and that means it could speed up your development speed.

References:
https://github.com/go-playground/validator
https://godoc.org/gopkg.in/go-playground/validator.v10
https://image.freepik.com/free-vector/document-checklist-paper-sheets-pile-with-tick-flat-design_115464-592.jpg
https://camo.githubusercontent.com/98ed65187a84ecf897273d9fa18118ce45845057/68747470733a2f2f7261772e6769746875622e636f6d2f676f6c616e672d73616d706c65732f676f706865722d766563746f722f6d61737465722f676f706865722e706e67

--

--