import * as z from 'zod';
import * as v from 'valibot';
// Zod - Method chaining (Convenabil, dar nu e tree-shakable)
const ZodUserSchema = z.object({
id: z.string().uuid(),
age: z.number().min(18),
});
// Valibot - Functional (Tree-shakable, footprint minim)
const ValibotUserSchema = v.object({
id: v.pipe(v.string(), v.uuid()),
age: v.pipe(v.number(), v.minValue(18)),
});Salutare tuturor. Am ajuns la un punct de cotitură cu un API de Node.js/TypeScript rulat pe AWS Lambda, unde avem în jur de 50 de schema-uri de validare destul de stufos definite. Zod își face treaba impecabil pe DX, dar am început să simțim greutatea lui la cold start-uri și la dimensiunea pachetului de deployment.
Problema cu Zod când crește proiectul
Când ai zeci de DTO-uri cu .transform(), .refine() și structuri imbricate, Zod adaugă binișor la bundle size. Am făcut un audit recent cu source-map-explorer și Zod singur ne trage în jos cu aproape 58KB (gzipped) în fiecare Lambda bundle.
Nu pare o tragedie la prima vedere. Totuși, pe instanțe de Lambda cu 512MB RAM, unde fiecare milisecundă de parsing și evaluare de modul contează la cold start, se adună. Problemă principală cu Zod este că nu e tree-shakable prin design: dacă imporți o singură schemă simplă, tragi tot runtime-ul după tine.
Ce am obținut în PoC-ul cu Valibot
Săptămâna trecută am luat 5 endpoint-uri mai grele și le-am rescris pe Valibot ca să văd cifre reale. Rezultatele pe bundle au fost incredibile: am scăzut de la 62KB la doar 14KB per funcție izolată. Pe partea de parsing pur, pentru un payload JSON de 2MB cu 8.000 de obiecte în array, Valibot a fost cu ~25% mai rapid decât Zod 3.x.
Sintaxa funcțională din Valibot permite bundler-ului (esbuild în cazul nostru) să elimine absolut tot ce nu folosești. Dacă nu folosești validare de email în Lambda X, codul pentru email validation nici nu ajunge în build.
Trade-off-urile care mă țin în loc
Prima chestie e DX-ul. Chaining-ul din Zod (z.string().min(3).email()) e mult mai lizibil și natural când scrii cod rapid. În Valibot, abordarea funcțională (v.pipe(v.string(), v.minLength(3), v.email())) devine cam încărcată când ai un obiect cu 30 de câmpuri și reguli custom.
A doua problemă e ecosistemul. Noi folosim intensiv zod-to-openapi pentru a genera documentația Swagger automat direct din cod. La Valibot există @valibot/to-json-schema, dar integrarea cu OpenAPI 3.1 pe tipuri de unire complexe (discriminated unions) mai dă rateuri și necesită workaround-uri manuale.
Ce mă interesează din experiența voastră
A făcut cineva mutarea asta completă în producție pe un API de dimensiune medie? Ati găsit blocaje pe inferența de tipuri TypeScript complexe sau în integrarea cu alte librării din ecosistem? Mă întreb dacă câștigul de performanță justifică cele 3-4 zile de muncă pentru refactorizarea tuturor celor 50 de schema-uri.