Executando verificação de segurança...
2

Validação de Dados em GO com gookit/validate

No universo de JavaScript uma das bibliotecas mais populares para validação de dados com certeza é o zod, mas quando iniciei meus estudos em GO vi que a forma de validar os dados de forma nativa era um tanto quanto engessada.

Após muita pesquisa encontrei um pacote para GO chamado gookit/validate, uma forma muito mais prática e simples de validar os dados, porém ainda não era completamente o que eu estava buscando. Eu queria algo que trouxesse uma experiência de desenvolvimento mais simples e com um código ainda reutilizável. Para isso desenvolvi um validador de dados utilizando como core o gookit/validate.

Neste artigo apresento o meu validador e como utilizá-lo de forma simples em um handler que será posteriormente utilizado em uma chamada HTTP para validar os dados de entrada do usuário.

1 — Struct:

type NewValidator struct {
 Data interface{}
 Validator *validate.Validation
}

A propriedade Data como o próprio nome já diz irá armazenar os dados que serão validados. O Validator irá armazenar o validate do pacote gookit/validate para ser utilizado nas validações posteriormente.

2 — Initializer:

func (nv *NewValidator) Initializer() {
 v := validate.Struct(nv.Data)
 v.StopOnError = false

 nv.Validator = v
}

O Initializer serve para adicionar os dados que serão validados para o validate, utilizando o método Struct. Também utilizei o método StopOnError passando o valor false para ele. Este método serve para definir se iremos retornar todos ou apenas um erro por vez, essa configuração influencia no uso do método All() do Validator. Por fim é atribuído o validate criado para a propriedade Validator do struct NewValidator.

3 — Método GetErrors

func (nv *NewValidator) GetErrors() []string {
 v := nv.Validator

 errors_msg := []string{}

 for field := range v.Errors.All() {
  errors := v.Errors.Field(field)
  for _, err := range errors {
   errors_msg = append(errors_msg, err)
  }
 }

 return errors_msg
}

O método GetErrors é muito simples e serve para retornar todos os erros dos campos que foram validados.
A variável v armazena o Validator que será utilizado para extrair os erros dos campos. Com isso, utilizando o método All() a partir de v.Errors é feita uma iteração sobre o map retornado por esse método.
O field em questão retorna a key para acessarmos o map de erros do campo que estamos extraindo as mensagens de erro, logo, temos uma nova variável chamada errors que armazena um map de erros do campo, utilizando o método Fields() a partir de v.Errors passando como valor a key do map.
Após esta etapa é feita uma nova iteração agora na variável errors, e, então é armazenado cada string retornada no array da variável errors_msg criada antes do primeiro laço for.
Por fim, o método GetErrors retorna a variável errors_msg a qual contém todas as mensagens de erro extraídas.

Validações

Os campos que serão validados devem ser adicionados no struct, conforme o exemplo:

type ProductDto struct {
 Name        string  `json:"name" validate:"required" message:"O nome do produto é obrigatório"`
 Description string  `json:"description"`
 Price       float64 `json:"price" validate:"required|float|min:1" message:"O preço do produto é obrigatório"`
}

Caso de Uso

body := models.ProductDto{}

if err := context.BindJSON(&body); err != nil {
  return models.Product{}, models.NewError("Server Erro", http.StatusInternalServerError, []string{})
}

v := utils.NewValidator{Data: body}

v.Initializer()

if (!v.Validator.Validate()) {
  return models.Product{}, models.NewError("Fields Validation Error", http.StatusBadRequest, v.GetErrors())
}

No exemplo acima foi criada a variável body a qual irá armazenar os dados enviados pelo usuário no corpo da requisição. Essa variável precisa ser do tipo struct criado anteriormente, neste caso o ProductsDTO. Após isso, é criada a variável v que irá armazenar o nosso validator para ser utilizado nas validações dos dados enviados pelo usuário.
O método Validate está sendo utilizado para validar se há algum erro de validação, este método retorna true ou false.
Por fim, para retornar a lista de erros, basta chamar o método GetErrors.

Gostou? Tem alguma outra ideia? Deixe seu comentário.

Carregando publicação patrocinada...
2

Ideia muito boa! O Zod é realmente um diferencial.
Comecei a estudar Go recentemente e logo senti falta de uma ferramenta assim pra validação.
Vou testar sua ideia pra ver a diferença — acredito que continuar aprimorando vai deixar a linguagem ainda mais poderosa.

1

Fico feliz que tenha gostado da ideia, espero que atenda bem ao seu caso de uso. Realmente GO é uma linguagem muito poderosa, mas a comunidade ainda está em crescimento, com certeza com o tempo ela irá crescer e novas funcionalidades serão adicionadas.

1

Entendi o seu ponto de vista, somente um ponto que discordo de maneira árdua.

Se está aprendendo uma linguagem nova ainda mais sendo Go, não traga os vícios de linguagem de outra linguagem, não é por que é mais verboso ou enroscado que é ruim.

Uma das principais vantagens do go é que a std lib da linguagem é recheada de funções, se tu está aprendendo eu recomendaria usar ao máximo a std.

Não que não possa usar libs externas mas se for usar é por sua conta e risco, creio que a comunidade está em ascensão então a possibilidade de ter várias libs feitas por pessoas que não dominam plenamente a linguagem é bem alta, pode perder performance por querer algo mais bonitinho.