Skip to content

How to use validate struct using package go-playground/validator/v10 in golang?

This project example is an example of a validator struct in golang, using package go-playground/validator/v10.

Project Initialization

Let’s start by creating a new directory for our project:

mkdir go-validator && cd go-validator

initialize our Go module:

go mod init github.com/adamnasrudin03/go-validator

Now from the root of the project, let’s create 3 subdirectories:

mkdir dto controllers helpers

install package validator:

 go get github.com/go-playground/validator/v10

Create DTO Structure

create new file in directory dto, name it user.go and add the following code:

package dto

type UserRequest struct {
	Name    string `json:"name" validate:"required,min=4"`
	Email   string `json:"email" validate:"required,email"`
	Age     uint64 `json:"age" validate:"gte=18,lte=120"`
	Address string `json:"address" validate:"required,min=10"`
	Phone   string `json:"phone" validate:"required,e164"` // https://pkg.go.dev/github.com/go-playground/validator/v10#hdr-E_164_Phone_Number_String
}

Helpers response Http

In directory helpers, create new directory response, then create new file name it response.go and add the following code:

package response

import (
	"encoding/json"
	"errors"
	"net/http"
)

type Response struct {
	Message interface{} `json:"message,omitempty"`
	Errors  interface{} `json:"errors,omitempty"`
	Data    interface{} `json:"data,omitempty"`
}

func NewResponse(input interface{}) (resp Response) {
	switch input := input.(type) {
	case []string:
		resp = Response{
			Errors: input,
		}
	case error:
		resp = Response{
			Errors: input.Error(),
		}
	case string:
		resp = Response{
			Message: input,
		}
	default:
		resp = Response{
			Data: input,
		}
	}

	return resp
}

func APIResponse(w http.ResponseWriter, r *http.Request, data interface{}, code int) {
	jsonBytes, err := json.Marshal(NewResponse(data))
	if err != nil {
		APIResponseInternalServerError(w, r)
		return
	}
	w.WriteHeader(code)
	w.Write(jsonBytes)
}

func APIResponseInternalServerError(w http.ResponseWriter, r *http.Request) {
	APIResponse(w, r, errors.New(http.StatusText(http.StatusInternalServerError)), http.StatusInternalServerError)
}

func APIResponseNotFound(w http.ResponseWriter, r *http.Request) {
	APIResponse(w, r, errors.New(http.StatusText(http.StatusNotFound)), http.StatusNotFound)
}

func PanicRecover(w http.ResponseWriter, r *http.Request) {
	if rc := recover(); rc != nil {
		APIResponseInternalServerError(w, r)
	}
}

Helpers function handler custom error validator

create new file in directory helpers, create directory name it validator then create new file name it errors.go and add the following code:

package validator

import (
	"fmt"

	"github.com/go-playground/validator/v10"
)

func FormatValidationError(err error) []string {
	if fieldErrors, ok := err.(validator.ValidationErrors); ok {
		resp := make([]string, len(fieldErrors))

		for i, e := range fieldErrors {
			switch e.Tag() {
			case "required":
				resp[i] = fmt.Sprintf("%s is a required field", e.Field())
			case "email":
				resp[i] = fmt.Sprintf("%v must be type %v", e.Field(), e.Tag())
			case "e164":
				resp[i] = fmt.Sprintf("%v must be type phone number", e.Field())
			case "gte":
				resp[i] = fmt.Sprintf("%v must be greater than or equal to %v", e.Field(), e.Param())
			case "lte":
				resp[i] = fmt.Sprintf("%v must be less than or equal to %v", e.Field(), e.Param())
			default:
				resp[i] = fmt.Sprintf("%v is %v %v", e.Field(), e.Tag(), e.Param())
			}
		}

		return resp
	}

	return []string{}
}

Create Handler Http Request

create new file in directory controllers, name it user.go and add the following code:

package controllers

import (
	"encoding/json"
	"errors"
	"io"
	"net/http"

	"github.com/adamnasrudin03/go-validator/dto"
	"github.com/adamnasrudin03/go-validator/helpers/response"
	help_validator "github.com/adamnasrudin03/go-validator/helpers/validator"
	"github.com/go-playground/validator/v10"
)

type UserController interface {
	HandlerCustomValidation(w http.ResponseWriter, r *http.Request)
}

type userHandler struct {
	Validate *validator.Validate
}

func NewUserController(validate *validator.Validate) UserController {
	return &userHandler{
		Validate: validate,
	}
}

func (h *userHandler) HandlerCustomValidation(w http.ResponseWriter, r *http.Request) {
	defer response.PanicRecover(w, r)
	reqBody, err := io.ReadAll(r.Body)
	if err != nil {
		response.APIResponse(w, r, errors.New("invalid request"), http.StatusBadRequest)
		return
	}

	input := dto.UserRequest{}
	err = json.Unmarshal(reqBody, &input)
	if err != nil {
		response.APIResponse(w, r, errors.New("invalid request"), http.StatusBadRequest)
		return
	}

	// validate input
	err = h.Validate.Struct(input)
	if err != nil {
		response.APIResponse(w, r, help_validator.FormatValidationError(err), http.StatusBadRequest)
		return
	}

	response.APIResponse(w, r, input, http.StatusOK)
}

Create Main Function

in root directory, create new file name it main.go and add the following code:

package main

import (
	"log"
	"net/http"

	"github.com/adamnasrudin03/go-validator/controllers"
	"github.com/adamnasrudin03/go-validator/helpers/response"
	"github.com/go-playground/validator/v10"
)

func main() {
	port := "1200"
	mux := http.NewServeMux()
	validator := validator.New()
	userController := controllers.NewUserController(validator)

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("content-type", "application/json")
		defer response.PanicRecover(w, r)

		switch {
		case r.URL.Path == "/" && r.Method == "GET":
			response.APIResponse(w, r, "Welcome my application", http.StatusOK)

		case r.URL.Path == "/custom-validation" && r.Method == "POST":
			userController.HandlerCustomValidation(w, r)

		default:
			response.APIResponseNotFound(w, r)
		}
	})

	log.Printf("Server is running on port %v\n", port)
	err := http.ListenAndServe(":"+port, mux)
	if err != nil {
		log.Fatal(err)
	}

}

Usage and testing example

open terminal and run following command:

go run main.go

test endpoint using postman or other tools, example response:

  • All field errors test endpoint All field errors
  • Two field errors test endpoint Two field errors
  • Success test endpoint Success

Conclusion

This is an example of Go validator struct validation within a Golang project. For other validation options or documentation, refer to the go-playground/validator/v10 package. If you'd like to see the full code, you can visit github.com/adamnasrudin03/go-validator. Enjoy and happy coding!