Concepto clave
En SvelteKit, los Server Actions son funciones que se ejecutan exclusivamente en el servidor, permitiendo manejar operaciones sensibles como validación de datos, acceso a bases de datos y envío de emails sin exponer lógica al cliente. Imagina un restaurante donde el cliente solo ve el menú y hace su pedido (frontend), pero toda la preparación, cocción y empaquetado ocurre en la cocina (backend). Los Server Actions son como esos procesos de cocina: seguros, eficientes y ocultos al cliente.
La validación en este contexto va más allá de simples comprobaciones en el navegador. Implementamos una validación en dos capas: primero en el cliente para experiencia de usuario (feedback inmediato) y luego en el servidor para seguridad (validación definitiva). Esto es crucial porque el usuario podría deshabilitar JavaScript o manipular las validaciones del cliente.
Cómo funciona en la práctica
Vamos a construir un formulario de contacto completo paso a paso. Primero, creamos la estructura básica del formulario en Svelte con campos para nombre, email y mensaje. Luego, implementamos validación del lado del cliente usando las capacidades nativas de HTML5 y algo de JavaScript para feedback visual.
El siguiente paso es crear la Server Action en +page.server.js. Esta función recibirá los datos del formulario, realizará validación exhaustiva (incluyendo sanitización) y luego procesará la lógica de negocio. Finalmente, conectamos el formulario del cliente con la Server Action usando el atributo action de SvelteKit, que maneja automáticamente el envío y la respuesta.
Código en acción
Primero, el formulario básico en +page.svelte:
<form method="POST" action="?/sendContact">
<div>
<label for="name">Nombre completo</label>
<input
type="text"
id="name"
name="name"
required
minlength="2"
maxlength="100"
>
<span class="error">{@errors.name}</span>
</div>
<div>
<label for="email">Email</label>
<input
type="email"
id="email"
name="email"
required
>
<span class="error">{@errors.email}</span>
</div>
<div>
<label for="message">Mensaje</label>
<textarea
id="message"
name="message"
required
minlength="10"
maxlength="1000"
></textarea>
<span class="error">{@errors.message}</span>
</div>
<button type="submit">Enviar mensaje</button>
</form>Ahora, la Server Action en +page.server.js:
import { fail } from '@sveltejs/kit';
import { z } from 'zod';
export const actions = {
sendContact: async ({ request }) => {
const formData = await request.formData();
// Esquema de validación con Zod
const contactSchema = z.object({
name: z.string()
.min(2, 'El nombre debe tener al menos 2 caracteres')
.max(100, 'El nombre no puede exceder 100 caracteres')
.trim(),
email: z.string()
.email('Email inválido')
.max(150, 'Email demasiado largo'),
message: z.string()
.min(10, 'El mensaje debe tener al menos 10 caracteres')
.max(1000, 'El mensaje no puede exceder 1000 caracteres')
.trim()
});
try {
const validatedData = contactSchema.parse({
name: formData.get('name'),
email: formData.get('email'),
message: formData.get('message')
});
// Aquí iría la lógica de negocio real
// Ejemplo: await sendEmail(validatedData);
// Ejemplo: await saveToDatabase(validatedData);
console.log('Datos validados:', validatedData);
return { success: true, message: 'Mensaje enviado correctamente' };
} catch (error) {
if (error instanceof z.ZodError) {
const errors = error.flatten().fieldErrors;
return fail(400, {
errors: {
name: errors.name?.[0] || '',
email: errors.email?.[0] || '',
message: errors.message?.[0] || ''
}
});
}
return fail(500, {
errors: { general: 'Error interno del servidor' }
});
}
}
};Errores comunes
- Confiar solo en validación del cliente: Los usuarios pueden deshabilitar JavaScript o manipular las validaciones. Siempre valida en el servidor.
- No sanitizar los datos: Usar
.trim()en strings evita espacios innecesarios y potenciales problemas de almacenamiento. - Manejo pobre de errores: No capturar excepciones en las Server Actions puede causar caídas silenciosas. Usa try-catch siempre.
- Exponer detalles de error al cliente: En producción, muestra mensajes genéricos como "Error al procesar" en lugar de detalles técnicos.
- Olvidar el atributo method="POST": Sin él, el formulario se envía por GET, exponiendo datos en la URL.
Checklist de dominio
- Creé un formulario con validación HTML5 básica (required, minlength, type="email")
- Implementé una Server Action que recibe y procesa datos del formulario
- Usé una librería de validación (Zod, Yup, o similar) para validación robusta en servidor
- Manejé errores de validación mostrando mensajes específicos por campo
- Saniticé los datos de entrada (trim, escape cuando sea necesario)
- Protegí la Server Action contra errores inesperados con try-catch
- Proveí feedback claro al usuario sobre el resultado del envío
Extiende el formulario de contacto con campo de teléfono y notificación por email
Objetivo: Ampliar el formulario de contacto para incluir un campo de teléfono opcional y simular el envío de un email de confirmación.
- Agrega el campo teléfono al formulario en
+page.svelte:- Campo opcional (no required)
- Validación HTML5: type="tel" y pattern para formato internacional
- Muestra un ejemplo de formato en placeholder
- Actualiza la Server Action en
+page.server.js:- Modifica el esquema Zod para incluir teléfono como campo opcional
- Agrega validación: máximo 20 caracteres, solo números, espacios y signos +-()
- Si hay teléfono, normalízalo (elimina espacios y caracteres especiales)
- Simula envío de email:
- Crea una función
simulateEmailSendque retorne una promesa - Simula un retraso de 1-2 segundos con
setTimeout - Llama esta función después de la validación exitosa
- Maneja posibles errores en el envío del email
- Crea una función
- Mejora la UI:
- Muestra un indicador de carga mientras se procesa el envío
- Agrega un mensaje de éxito/error más detallado
- Permite reenviar el formulario después de un error
- Usa
z.string().optional()en Zod para campos no requeridos - Para el pattern del teléfono, considera
pattern="[+]?[0-9\s\-()]+" - Usa
form.processingen Svelte para mostrar indicador de carga
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.