Voltar
Por que você deve usar Zod com TypeScript: Um guia completo para aplicações web modernas
Descubra como Zod pode revolucionar sua validação de dados em TypeScript, oferecendo type safety em tempo de execução e desenvolvimento.
O TypeScript revolucionou o desenvolvimento JavaScript ao adicionar tipagem estática, mas ainda enfrentamos desafios quando se trata de validação de dados em tempo de execução. É aqui que o Zod entra em cena, oferecendo uma solução elegante e poderosa para validação de esquemas.
O problema da validação de dados
Em aplicações web modernas, frequentemente lidamos com dados vindos de APIs externas, formulários de usuário ou configurações. Mesmo com TypeScript, não temos garantias de que os dados em tempo de execução correspondam aos tipos que definimos.
interface User {
id: number;
name: string;
email: string;
age?: number;
}
// Este código pode falhar em tempo de execução
function processUser(data: any): User {
return data; // Sem validação!
}
O que é Zod?
Zod é uma biblioteca de validação de esquemas TypeScript-first que permite definir esquemas de validação que também geram tipos TypeScript automaticamente. É uma ferramenta essencial para qualquer desenvolvedor que queira garantir type safety tanto em tempo de desenvolvimento quanto em tempo de execução.
Vantagens do Zod
1. Type Safety Completo
import { z } from "zod";
// Define o esquema
const UserSchema = z.object({
id: z.number(),
name: z.string().min(1, "Nome é obrigatório"),
email: z.string().email("Email inválido"),
age: z.number().min(0).optional(),
});
// TypeScript infere o tipo automaticamente
type User = z.infer<typeof UserSchema>;
2. Validação em Tempo de Execução
// Validação segura
function processUser(data: unknown): User {
const validatedData = UserSchema.parse(data);
return validatedData;
}
// Ou com tratamento de erro
function processUserSafe(data: unknown) {
const result = UserSchema.safeParse(data);
if (!result.success) {
console.error("Dados inválidos:", result.error);
return null;
}
return result.data;
}
Casos de Uso Comuns
Validação de Formulários
const LoginFormSchema = z.object({
email: z.string().email("Email inválido"),
password: z.string().min(8, "Senha deve ter pelo menos 8 caracteres"),
rememberMe: z.boolean().optional(),
});
function handleLogin(formData: FormData) {
const data = Object.fromEntries(formData);
const validatedData = LoginFormSchema.parse(data);
// Agora temos certeza de que os dados são válidos
loginUser(validatedData);
}
Validação de APIs
const ApiResponseSchema = z.object({
success: z.boolean(),
data: z.array(UserSchema),
total: z.number(),
page: z.number().positive(),
});
async function fetchUsers(page: number) {
const response = await fetch(`/api/users?page=${page}`);
const rawData = await response.json();
// Valida a resposta da API
const validatedData = ApiResponseSchema.parse(rawData);
return validatedData;
}
Transformação de Dados
const StringToNumberSchema = z.string().transform((val) => parseInt(val, 10));
const DateStringSchema = z.string().transform((val) => new Date(val));
const ConfigSchema = z.object({
port: StringToNumberSchema,
createdAt: DateStringSchema,
features: z.string().transform((val) => val.split(",")),
});
Recursos Avançados
Validação Condicional
const ConditionalSchema = z.object({
type: z.enum(["admin", "user"]),
permissions: z.array(z.string()).optional(),
}).refine((data) => {
if (data.type === "admin") {
return data.permissions && data.permissions.length > 0;
}
return true;
}, {
message: "Admins devem ter pelo menos uma permissão",
});
Schemas Recursivos
const TreeNodeSchema: z.ZodType<TreeNode> = z.lazy(() =>
z.object({
id: z.number(),
name: z.string(),
children: z.array(TreeNodeSchema).optional(),
})
);
Validação de Arrays
const TagsSchema = z.array(
z.string().min(1).max(50)
).min(1, "Pelo menos uma tag é obrigatória")
.max(10, "Máximo 10 tags permitidas");
Integração com Frameworks
Next.js
// app/api/users/route.ts
import { z } from "zod";
const CreateUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
export async function POST(request: Request) {
try {
const body = await request.json();
const validatedData = CreateUserSchema.parse(body);
// Processa dados válidos
const user = await createUser(validatedData);
return Response.json({ success: true, user });
} catch (error) {
if (error instanceof z.ZodError) {
return Response.json({
success: false,
errors: error.errors
}, { status: 400 });
}
throw error;
}
}
React Hook Form
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
const FormSchema = z.object({
name: z.string().min(1, "Nome é obrigatório"),
email: z.string().email("Email inválido"),
});
type FormData = z.infer<typeof FormSchema>;
function MyForm() {
const form = useForm<FormData>({
resolver: zodResolver(FormSchema),
});
// Resto do componente...
}
Performance e Bundle Size
O Zod é otimizado para performance e tem um bundle size relativamente pequeno (~12KB gzipped). Para projetos que precisam de ainda menos código, você pode usar tree-shaking:
// Importe apenas o que você precisa
import { z } from "zod";
import { string, number, object } from "zod";
Boas Práticas
1. Reutilize Schemas
// schemas/user.ts
export const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
});
// schemas/api.ts
export const ApiResponseSchema = <T extends z.ZodTypeAny>(schema: T) =>
z.object({
success: z.boolean(),
data: schema,
message: z.string().optional(),
});
2. Use Error Handling
function validateData<T>(schema: z.ZodSchema<T>, data: unknown): T | null {
try {
return schema.parse(data);
} catch (error) {
if (error instanceof z.ZodError) {
console.error("Erro de validação:", error.errors);
}
return null;
}
}
3. Combine com TypeScript
// Use Zod para validação de runtime
const ConfigSchema = z.object({
apiUrl: z.string().url(),
timeout: z.number().positive(),
});
// Use TypeScript para tipagem estática
type Config = z.infer<typeof ConfigSchema>;
// Valide em tempo de execução
function loadConfig(): Config {
const config = process.env.CONFIG ? JSON.parse(process.env.CONFIG) : {};
return ConfigSchema.parse(config);
}
Conclusão
O Zod é uma ferramenta essencial para desenvolvedores TypeScript que querem garantir type safety em tempo de execução. Ele oferece:
- Type Safety: Inferência automática de tipos
- Validação Robusta: Validação em tempo de execução
- Flexibilidade: Transformação e validação condicional
- Performance: Bundle size otimizado
- Integração: Funciona perfeitamente com frameworks populares
Ao incorporar Zod em seu workflow de desenvolvimento, você elimina uma grande classe de bugs relacionados à validação de dados e melhora significativamente a confiabilidade de suas aplicações.
Dica: Comece com schemas simples e gradualmente adicione validações mais complexas conforme necessário. O Zod é poderoso, mas também pode ser simples quando você precisa.
Espero que este guia tenha ajudado você a entender os benefícios do Zod com TypeScript. Até a próxima! 🚀