O Zod é uma biblioteca de declaração e validação de dados, ou “schema validation” em TypeScript. Dessa forma, é possível criar uma estrutura com os requisitos de dados que você deseja validar.
Curso TypeScript - Fundamentos
Conhecer o cursoO interessante ao usar o Zod é que ele permite definir tipos de dados utilizando uma sintaxe concisa, garantindo que apenas dados válidos sejam aceitos, o que assegura a segurança do código. Além disso, possui integração com o TypeScript, permitindo o uso de tipos estáticos e inferência de tipos.
Vamos instalar o Zod e criar um exemplo para compreender como o Zod funciona. Para isso, é necessário, primeiramente, criar uma pasta e executar o comando:
npm init -y
Desta forma vamos inicializar um novo projeto e será criado o arquivo package.json
.
Agora podemos instalar o TypeScript executando o comando:
npm i --dev typescript
Por fim vamos executar o comando para criar o arquivo tsconfig.json
:
npx tsc --init
Neste momento podemos fazer a instalação do Zod propriamente dita:
npm install zod
Perfeito, agora podemos criar um exemplo.
Utilizando o Zod para validação de dados
O primeiro passo é criar o “schema”, ou seja, a estrutura que verificará se os dados que estamos tentando validar são válidos. Podemos criar um “schema” que valide os dados de um objeto da seguinte forma:
import { z } from "zod";
const carroSchema = z.object({
modelo: z.string(),
ano: z.number().min(1990).max(2023),
fabricante: z.string(),
cor: z.string().optional()
});
Veja que podemos criar uma estrutura onde temos os atributos e os tipos que esses atributos devem ter. No atributo “ano”, além do tipo, definimos que deve ser um número no intervalo de 1990 até 2023. Outro ponto é o atributo “cor”, que pode ser opcional. Ou seja, o Zod permite que, além de definir a estrutura de tipos, também possamos criar validações mais específicas e complexas.
Curso TypeScript - Avançado
Conhecer o cursoAgora, vamos completar este arquivo recebendo um objeto “carro” e validando se as informações estão corretas e como o Zod se comporta:
import { z } from "zod";
const carroSchema = z.object({
modelo: z.string(),
ano: z.number().min(1990).max(2023),
fabricante: z.string(),
cor: z.string().optional()
});
const carro = {
modelo: "Fiesta",
ano: 2000,
fabricante: "Ford",
cor: "Azul",
};
const validationResult = carroSchema.safeParse(carro); //
if(!validationResult.success) {
console.log(validationResult.error);
} else {
console.log(validationResult.data);
}
O método safeParse()
retorna um objeto com a seguinte estrutura caso a validação ocorra com sucesso:
{
success: true,
data: { modelo: 'Fiesta', ano: 2000, fabricante: 'Ford', cor: 'Azul' }
}
Ou caso ocorra um erro de validação:
{ success: false, error: [Getter] }
Ou seja, podemo utilizar a constante validationResult
e tratar caso ocorra algum problema, como fizemos acima utilizando o condicional if
, desta forma, caso success
seja falso, vamos obter o seguinte retorno na propriedade error
:
[
{
"code": "invalid_type",
"expected": "number",
"received": "string",
"path": [
"ano"
],
"message": "Expected number, received string"
}
]
Neste caso, o campo “ano” recebeu uma string
em vez de um dado do tipo number
. Observe que o Zod nos informa exatamente o que ocorreu, o que é muito interessante!
Dessa forma, você pode tratar os erros da maneira que for necessária para a sua aplicação. Outra observação importante é que há o método parse()
, no entanto, esse método lança uma exceção caso a validação não ocorra com sucesso.
Curso Nest.js - Fundamentos
Conhecer o cursoPodemos também diretamente no carroSchema
definir mensagens customizadas para a nossa validação e utilizar a inferência de tipo para criar o tipo carro
já com as validação, veja como:
import { z } from "zod";
const carroSchema = z.object({
modelo: z.string({
required_error: "modelo é obrigatório",
invalid_type_error: "modelo deve ser uma string",
}),
ano: z.number({
required_error: "ano é obrigatório",
invalid_type_error: "ano deve ser um número"
}).min(1990).max(2023),
fabricante: z.string({
required_error: "fabricante é obrigatório",
invalid_type_error: "fabricante deve ser uma string"
}),
cor: z.string({
invalid_type_error: "cor deve ser uma string"
}).optional()
});
type ValidateCarroPayload = z.infer<typeof carroSchema>;
const carro: ValidateCarroPayload = {
modelo: "Fiesta",
ano: 2005,
fabricante: "Ford",
cor: "Prata"
}
const result = carroSchema.safeParse(carro)
console.log(result);
Se a validação ocorrer com sucesso:
{
success: true,
data: { modelo: 'Fiesta', ano: 2005, fabricante: 'Ford', cor: 'Prata' }
}
Quando estamos trabalhando com validações personalizadas podemos deixar as validações mais complexas, como por exemplo validar se dois campos são iguais, reaproveitando o exemplo acima, podemos adicionar o campo renavam
para demostrar essa validação:
import { z } from "zod";
const carroSchema = z.object({
modelo: z.string({
required_error: "modelo é obrigatório",
invalid_type_error: "modelo deve ser uma string",
}),
ano: z.number({
required_error: "ano é obrigatório",
invalid_type_error: "ano deve ser um número"
}).min(1990).max(2023),
fabricante: z.string({
required_error: "fabricante é obrigatório",
invalid_type_error: "fabricante deve ser uma string"
}),
renavam: z.number({
required_error: "renavam é obrigatório",
invalid_type_error: "renavam deve ter somente números",
}),
renavamConfirmacao: z.number(),
cor: z.string({
invalid_type_error: "cor deve ser uma string"
}).optional()
}).refine((data) =>
data.renavam === data.renavamConfirmacao, { message: "Renavam não está igual" });
type ValidateCarroPayload = z.infer<typeof carroSchema>;
const carro: ValidateCarroPayload = {
modelo: "Fiesta",
ano: 2015,
fabricante: "Ford",
cor: "Prata",
renavam: 123456,
renavamConfirmacao: 321654,
}
const result = carroSchema.safeParse(carro)
if(!result.success) {
console.log(result.error.errors);
} else {
console.log(result.success);
}
Seguindo a mesma lógica do exemplo acima, ao tratar o retorno dentro do condicional if
para exibir os erros (acessando a propriedade errors
), no caso de ocorrer algum problema na validação, vamos obter o seguinte resultado:
[
{
"code": "custom",
"message": "Renavam não está igual",
"path": []
}
]
Veja que apontou o erro na validação da propriedade renavam
.
Trabalhando com “inputs” e “outputs”
Um recurso interessante do Zod é a possibilidade de trabalhar com tipos diferentes de entrada e saída, mas como isso funciona?
const numeroDeCaracteres = z.string().transform((palavra) => palavra.length);
type PalavraEntrada = z.input<typeof numeroDeCaracteres>;
type PalavraSaida = z.output<typeof numeroDeCaracteres>
const palavra: PalavraEntrada = 'Zod';
const numeroDeLetras: PalavraSaida = palavra.length;
console.log(palavra, numeroDeLetras); // Zod 3
console.log(numeroDeCaracteres.parse(palavra)); // 3
Veja que temos a constante numeroDeStrings
, essa constante recebe uma string que será convertida em um number. Podemos utilizar o recurso input e output para criar tipos diferentes a partir dessa constante e então aplicar conforme as necessidades da nossa aplicação e garantir que estamos trabalhando com os tipos corretos.
Outro recurso que podemos utilizar é sobre o método parse()
direto na constante palavra, que recebe uma string
e irá retornar, neste caso, um number
referente ao número de caracteres da palavra, conforme atribuido na constante numeroDeCaracteres
.
Conclusão
Este artigo é uma introdução básica à biblioteca de declaração e validação de dados Zod, onde aprendemos a instalar e utilizar os recursos básicos de validação de forma rápida e clara. Caso você queira aprofundar-se mais na utilização do Zod, aconselho verificar diretamente a documentação da biblioteca.
Por fim, caso queira aprender mais sobre TypeScript saiba que aqui na TreinaWeb temos o curso TypeScript - Fundamentos que possui 01h31 de vídeos e um total de 30 exercícios. Conheça também nossos outros cursos de TypeScript.
Veja quais são os tópicos abordados durante o curso de TypeScript - Fundamentos:
- Compreender melhor as vantagens que a utilização do TypeScript pode trazer;
- Utilizar a tipagem estática que o TypeScript traz;
- Utilizar o conceito de type assertion;
- Aplicar construção e desconstrução (ou decomposição) de objetos no TypeScript;
- Aplicar conceitos típicos de orientação a objetos, como classes, métodos acessores, interfaces e herança com o TypeScript;
- Verificar qual seria o código JavaScript que teria que ser produzido para se obter um efeito análogo à utilização do TypeScript.