Zod: O poder de validação e transformação de dados

Luís Miguel
4 min readMay 1, 2023

--

Exemplo de código na linguagem TypeScript posicionado a esquerda mostrando a utilização da biblioteca Zod, com a importação da biblioteca e a criação do objeto que será o schema para validação. Ao lado direito um diamante azul simbolizando a logo do Zod.

Hoje quero apresentar a você a biblioteca Zod, e expor algumas de suas funcionalidades como o poder que ela tem para validação e transformação de dados, além da sua incrível integração com o TypeScript! 🚀

Introdução

Validação de dados é algo muito comum tanto no front-end com formulários, quanto no back-end para tratar as entradas de rotas, visto que ela garante que todos os dados enviados estão no tipo e formato esperado, evitando possíveis erros.

Sabendo disso, é possível validar dados de forma mais simples e manual, fazendo suas próprias verificações e tratativas. No entanto, será que realmente vale a pena implementar uma validação manual do total zero? Acredito que não! 🤔

Por isso, existem diversas bibliotecas que auxiliam nesse processo, trazendo funcionalidades prontas e úteis, que são muito recorrentes em um processo de validação. Abaixo estão as mais utilizadas:

Apesar de nunca ter utilizado as outras, minha biblioteca de validação preferida é o Zod e vou te mostrar os motivos!

Zod

O Zod é uma biblioteca de validação e trasnformação TypeScript-first baseada em declarações de schema, ou seja, você pode definir uma estrutura com todos os valores que você deseja validar e os seus respectivos tipos, desde um tipo de dado mais simples como uma string, até estruturas de dados complexas.

Veja esse exemplo para validar um formato simples de string:

import { z } from 'zod'

// creating a schema for strings
const mySchema = z.string()

// parsing
mySchema.parse('tuna') // => 'tuna'
mySchema.parse(12) // => throws ZodError

// "safe" parsing (doesn't throw error if validation fails)
mySchema.safeParse('tuna') // => { success: true; data: 'tuna' }
mySchema.safeParse(12) // => { success: false; error: ZodError }

Além disso, o Zod tem o propósito de ser o mais amigável possível para os devs, proporcionado uma experiência de desenvolvimento muito simples.

O fato de ser TypeScript-first faz com que o Zod consiga inferir automaticamente tipos do TypeScript, evitando declarações de tipagem duplicadas. Observe a definição de um schema como objeto e a inferência que o Zod é capaz de fazer para obter a tipagem desse objeto:

import { z } from 'zod'

const userSchema = z.object({
username: z.string(),
email: z.string().email(),
})

userSchema.parse({
username: 'Bob',
email: 'bob@example.com',
})

// extract the inferred type
type User = z.infer<typeof userSchema> // { username: string, email: string }

Você lembra que eu também havia falado sobre transformação de dados? Uma das coisas mais incríveis do Zod é poder transformar os dados que estão sendo recebidos e retorná-los em um formato diferente. Observe esse exemplo mais completo:

import { z } from 'zod'

const userSchema = z.object({
id: z.string().uuid(),
avatar_url: z.string().url(),
name: z.string().trim().min(3).toLowerCase(),
username: z
.string()
.min(3)
.regex(/^([a-z-]+)$/i)
.transform((username) => `@${username}`),
email: z.string().email(),
password: z.string().min(6).max(18),
created_at: z.coerce.date(),
})

const userData = userSchema.parse({
id: 'abae30a9-7cc5-4c85-9c71-f592a9a2802f',
avatar_url: 'https://app.com/avatar.png',
name: 'John Doe', // transform string to lower case
username: 'john-doe', // add @ in string start
email: 'johndoe@example.com',
password: '123456',
created_at: '2023-05-01T11:28:00.335Z', // convert string to JavaScript Date
})

No exemplo acima, o método transform() foi utilizado para manipular o valor da propriedade e modificar o formato ou até mesmo o tipo. Além disso, a propriedade coerce serviu para converter o valor do campo created_at para um objeto Date do JavaScript, ou seja, caso esse campo receba um valor diferente de uma data o Zod tentará transformar em uma data.

Zod e React Hook Form

Outro motivo que me faz escolher o Zod como principal biblioteca para validação é o fato de ser muito simples e versátil, podendo ser integrado com outras bibliotecas como o React Hook Form.

O React Hook Form é uma biblioteca para manipulação de formulários no React que permite fazer uma integração com outras bibliotecas para criar validações nos campos dos formulários. Dentre as bibliotecas suportadas, o Zod pode ser utilizado por meio de um resolver que permitirá a integração. Veja o exemplo:

import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'

const userSchema = z.object({
name: z.string().trim().min(1, { message: 'Required' }),
age: z.number().min(16).optional(),
})

// { name: string, age: number | undefined }
type User = z.infer<typeof userSchema>

const App = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<User>({
resolver: zodResolver(userSchema),
})

return (
<form onSubmit={handleSubmit((d) => console.log(d))}>
<input {...register('name')} />
{errors.name?.message && <p>{errors.name?.message}</p>}
<input type="number" {...register('age', { valueAsNumber: true })} />
{errors.age?.message && <p>{errors.age?.message}</p>}
<input type="submit" />
</form>
)
}

Perceba que, na declaração do userSchema o campo age foi definido como opcional por meio do método optional(), o que fez com que o tipo fosse uma string ou undefined. Além desse método, existem os métodos nullable() que aceita um valor null, e nullish() que aceita os valores null e undefined.

Conclusão

Nesse post apresentei de forma mais geral um pouco das funcionalidades e vantagens do Zod como a sua forte integração com o TypeScript e sua capacidade de transformar dados, além de um breve exemplo de integração com o React Hook Form para validação de formulários no front-end.

Se você deseja conhecer tudo o que o Zod tem a oferecer, recomendo fortemente a leitura da documentação para mais detalhes.

Espero que você tenha gostado e nos vemos em um próximo post!

--

--

Luís Miguel

Full Stack Developer focused on JavaScript ecosystem technologies and passionate about programming.