← Volver al listado de tecnologías

Introducción a Valibot: Validación de Esquemas Moderna y Ligera

Por: Artiko
valibottypescriptvalidaciónesquemastype-safety

Introducción a Valibot: Validación de Esquemas Moderna y Ligera

¿Qué es Valibot?

Valibot es una biblioteca de validación de esquemas para TypeScript que se destaca por su diseño modular y su tamaño extremadamente pequeño. Creada por Fabian Hiller, Valibot ofrece una alternativa ligera a bibliotecas como Zod, con un enfoque en el rendimiento y la optimización del bundle size.

Características Principales

1. Ultra Ligero

2. Type-Safe

3. Modular

4. Rendimiento

Instalación

# npm
npm install valibot

# yarn
yarn add valibot

# pnpm
pnpm add valibot

# bun
bun add valibot

Primeros Pasos

1. Esquema Básico

import { object, string, number, email, minLength, minValue, parse } from 'valibot';

// Definir un esquema de usuario
const UserSchema = object({
  name: string([minLength(2, 'El nombre debe tener al menos 2 caracteres')]),
  email: string([email('Email inválido')]),
  age: number([minValue(18, 'Debe ser mayor de 18 años')])
});

// Usar el esquema
try {
  const validUser = parse(UserSchema, {
    name: 'Juan',
    email: '[email protected]',
    age: 25
  });
  console.log('Usuario válido:', validUser);
} catch (error) {
  console.error('Error de validación:', error);
}

2. Tipos Primitivos

import { string, number, boolean, date, parse } from 'valibot';

// String
const nameSchema = string();
const name = parse(nameSchema, 'Juan');

// Number
const ageSchema = number();
const age = parse(ageSchema, 25);

// Boolean
const activeSchema = boolean();
const isActive = parse(activeSchema, true);

// Date
const birthDateSchema = date();
const birthDate = parse(birthDateSchema, new Date());

3. Arrays y Objetos

import { array, object, string, number } from 'valibot';

// Array de strings
const tagsSchema = array(string());

// Objeto anidado
const ProductSchema = object({
  id: number(),
  name: string(),
  tags: array(string()),
  details: object({
    description: string(),
    price: number()
  })
});

Validaciones Comunes

1. Strings

import { string, minLength, maxLength, length, regex, email, url } from 'valibot';

// Longitud mínima y máxima
const passwordSchema = string([
  minLength(8, 'Mínimo 8 caracteres'),
  maxLength(20, 'Máximo 20 caracteres')
]);

// Longitud exacta
const codeSchema = string([length(6, 'El código debe tener 6 caracteres')]);

// Expresión regular
const phoneSchema = string([
  regex(/^\+?[1-9]\d{1,14}$/, 'Número de teléfono inválido')
]);

// Email
const emailSchema = string([email('Email inválido')]);

// URL
const websiteSchema = string([url('URL inválida')]);

2. Números

import { number, minValue, maxValue, integer } from 'valibot';

// Rango de valores
const ageSchema = number([
  minValue(0, 'La edad no puede ser negativa'),
  maxValue(150, 'La edad no puede ser mayor a 150')
]);

// Números enteros
const quantitySchema = number([
  integer('Debe ser un número entero'),
  minValue(1, 'La cantidad mínima es 1')
]);

3. Opcionales y Valores por Defecto

import { object, string, optional, nullable, transform } from 'valibot';

const UserSchema = object({
  // Campo opcional
  nickname: optional(string()),
  
  // Campo nullable
  middleName: nullable(string()),
  
  // Valor por defecto
  role: transform(
    optional(string()),
    (value) => value || 'user'
  )
});

Validación de Formularios

Ejemplo con React

import { object, string, email, minLength, safeParse } from 'valibot';
import { useState } from 'react';

const LoginSchema = object({
  email: string([email('Email inválido')]),
  password: string([minLength(8, 'Mínimo 8 caracteres')])
});

function LoginForm() {
  const [errors, setErrors] = useState<Record<string, string>>({});

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    
    const formData = new FormData(e.currentTarget);
    const data = Object.fromEntries(formData);
    
    const result = safeParse(LoginSchema, data);
    
    if (result.success) {
      console.log('Login exitoso:', result.output);
      // Procesar login
    } else {
      const newErrors: Record<string, string> = {};
      result.issues.forEach(issue => {
        if (issue.path) {
          newErrors[issue.path[0].key] = issue.message;
        }
      });
      setErrors(newErrors);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input
          name="email"
          type="email"
          placeholder="Email"
        />
        {errors.email && <span>{errors.email}</span>}
      </div>
      
      <div>
        <input
          name="password"
          type="password"
          placeholder="Contraseña"
        />
        {errors.password && <span>{errors.password}</span>}
      </div>
      
      <button type="submit">Iniciar Sesión</button>
    </form>
  );
}

Schemas Avanzados

1. Uniones y Discriminated Unions

import { object, string, literal, union } from 'valibot';

// Union simple
const StatusSchema = union([
  literal('pending'),
  literal('active'),
  literal('inactive')
]);

// Discriminated union
const NotificationSchema = union([
  object({
    type: literal('email'),
    email: string([email()]),
    subject: string()
  }),
  object({
    type: literal('sms'),
    phone: string(),
    message: string()
  }),
  object({
    type: literal('push'),
    token: string(),
    title: string(),
    body: string()
  })
]);

2. Transformaciones

import { string, transform, trim, toLowerCase } from 'valibot';

// Transformación simple
const emailSchema = transform(
  string([email()]),
  (value) => value.toLowerCase().trim()
);

// Múltiples transformaciones
const usernameSchema = string([
  transform(trim()),
  transform(toLowerCase()),
  minLength(3)
]);

3. Validaciones Personalizadas

import { string, custom } from 'valibot';

// Validador personalizado
const strongPasswordSchema = string([
  minLength(8),
  custom((value) => {
    const hasUpperCase = /[A-Z]/.test(value);
    const hasLowerCase = /[a-z]/.test(value);
    const hasNumbers = /\d/.test(value);
    const hasSpecialChar = /[!@#$%^&*]/.test(value);
    
    return hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChar;
  }, 'La contraseña debe contener mayúsculas, minúsculas, números y caracteres especiales')
]);

Inferencia de Tipos

import { object, string, number, type Input, type Output } from 'valibot';

const UserSchema = object({
  id: number(),
  name: string(),
  email: string([email()])
});

// Inferir tipos de entrada y salida
type UserInput = Input<typeof UserSchema>;
type UserOutput = Output<typeof UserSchema>;

// Usar los tipos
function createUser(data: UserInput): UserOutput {
  return parse(UserSchema, data);
}

Comparación con Otras Bibliotecas

Valibot vs Zod

// Zod
import { z } from 'zod';
const schema = z.object({
  name: z.string().min(2)
});

// Valibot
import { object, string, minLength } from 'valibot';
const schema = object({
  name: string([minLength(2)])
});

Ventajas de Valibot:

Ventajas de Zod:

Mejores Prácticas

1. Organización de Esquemas

// schemas/user.ts
import { object, string, email, minLength } from 'valibot';

export const CreateUserSchema = object({
  name: string([minLength(2)]),
  email: string([email()]),
  password: string([minLength(8)])
});

export const UpdateUserSchema = partial(CreateUserSchema);

2. Reutilización de Validadores

// validators/common.ts
import { string, minLength, regex } from 'valibot';

export const passwordValidator = string([
  minLength(8, 'Mínimo 8 caracteres'),
  regex(/[A-Z]/, 'Debe contener al menos una mayúscula'),
  regex(/[a-z]/, 'Debe contener al menos una minúscula'),
  regex(/[0-9]/, 'Debe contener al menos un número')
]);

// Usar en múltiples esquemas
const LoginSchema = object({
  email: string([email()]),
  password: passwordValidator
});

3. Manejo de Errores

import { safeParse, flatten } from 'valibot';

function validateData(data: unknown) {
  const result = safeParse(UserSchema, data);
  
  if (!result.success) {
    const errors = flatten(result.issues);
    console.error('Errores de validación:', errors);
    return { success: false, errors };
  }
  
  return { success: true, data: result.output };
}

Conclusión

Valibot es una excelente opción para proyectos que requieren validación de esquemas con un enfoque en el rendimiento y el tamaño del bundle. Su diseño modular y su API funcional lo hacen ideal para aplicaciones modernas donde cada KB cuenta.

Recursos Adicionales

En el próximo tutorial, exploraremos casos de uso más avanzados y cómo integrar Valibot con diferentes frameworks y bibliotecas.