Saltar al contenido

Lección 7 de 17 · Lectura y práctica

Práctica: Crear un CRUD con Server Actions

Tipo de lección: VideoConcepto claveLas Server Actions en Next.js 15 son funciones que se ejecutan exclusivamente en el servidor, permitiendo operaciones de datos seguras sin exponer APIs públicas. Piensa en ellas como cajeros.

Del concepto a la práctica

Taller de esta lección

Un CRUD completo exige conservar la identidad del registro y separar las cuatro operaciones. Usaremos tareas locales con identificadores numéricos: leer [1,2], crear el id 3, actualizar el id 2 y eliminar el id 1. El identificador no debe venir de un título editable.

Ejemplo resuelto

ts
let tasks = [{ id: 1, text: 'Mapa' }, { id: 2, text: 'Pruebas' }]
export async function renameTask(id: number, text: string) {
  const task = tasks.find(t => t.id === id)
  if (!task || !text.trim()) return { ok: false }
  task.text = text.trim(); return { ok: true, task }
}

Renombrar 2 a Logs deja [Mapa, Logs].

Pruébalo

  1. Lista los dos registros iniciales.

  2. Crea un tercero con id único.

  3. Actualiza solo el registro 2.

  4. Elimina el 1 y vuelve a listar.

Comprueba tu resultado

Error frecuente: usar la posición del array como identidad. Corrígelo usando id. Resultado esperado final: quedan exactamente los registros 2 y 3; el 2 se llama Logs, y ninguna operación modifica accidentalmente el registro vecino.

Completa las cuatro operaciones del fixture

Este array mutable es una simulación de proceso: se pierde al reiniciar y no se comparte con otras instancias. No lo utilices como persistencia multiusuario. Un módulo de fixture marcado import 'server-only' puede exponer estas operaciones al servicio:

ts
let tasks = [{ id: 1, text: 'Mapa' }, { id: 2, text: 'Pruebas' }]
let nextId = 3
export function listTasks() { return tasks.map(t => ({ ...t })) }
export function createTask(text: string) {
  const value = text.trim(); if (!value) throw new Error('Texto requerido')
  const task = { id: nextId++, text: value }; tasks.push(task); return { ...task }
}
export function renameTask(id: number, text: string) {
  const i = tasks.findIndex(t => t.id === id)
  if (i < 0 || !text.trim()) return { ok: false as const }
  tasks[i] = { ...tasks[i], text: text.trim() }
  return { ok: true as const, task: { ...tasks[i] } }
}
export function deleteTask(id: number) {
  const i = tasks.findIndex(t => t.id === id); if (i < 0) return false
  tasks = tasks.filter(t => t.id !== id); return true
}

Parte de un proceso limpio: crea Evidencias (3), renombra 2 como Logs y elimina 1. listTasks devuelve 2/Logs y 3/Evidencias. Las Server Actions envuelven estas funciones después de comprobar sesión y tipos; revalida /tareas tras escribir. Para entregar el proyecto sustituye el fixture por almacenamiento duradero con autorización por registro.

Servidor que produce HTML y navegador que lo muestra y gestiona la interacción; claves sensibles permanecen en el servidor.
CursaloMapa de práctica
Dónde se ejecuta cada parte

El servidor puede obtener datos y producir el HTML inicial. El navegador muestra el resultado y ejecuta las interacciones que necesitan estado local. Mantén las claves sensibles en el servidor y define qué parte se ejecuta en cada entorno.

Ver la infografía en tamaño completo ↗
Fuentes para profundizar

Tipo de lección: Video

Concepto clave

Las Server Actions en Next.js 15 son funciones que se ejecutan exclusivamente en el servidor, permitiendo operaciones de datos seguras sin exponer APIs públicas. Piensa en ellas como cajeros automáticos: tu aplicación cliente (el usuario) inserta una solicitud (como retirar dinero), pero toda la lógica de validación y procesamiento ocurre dentro del servidor (el banco), manteniendo la seguridad de las credenciales y el saldo.

En un CRUD tradicional con APIs REST, necesitas crear endpoints separados para cada operación (GET, POST, PUT, DELETE), lo que puede volverse complejo. Con Server Actions, defines funciones directas en tus componentes de servidor que manejan creación, lectura, actualización y eliminación en un solo lugar, reduciendo la sobrecarga de configuración. Esto es especialmente útil en aplicaciones full-stack donde la velocidad y la seguridad son prioritarias.

Cómo funciona en la práctica

Imagina que estás construyendo una app de tareas. Primero, creas un componente de servidor en app/tasks/page.js. Dentro, defines Server Actions usando 'use server' al inicio de la función. Por ejemplo, para agregar una tarea:

'use server';
export async function addTask(formData) {
  const title = formData.get('title');
  // Validar y guardar en base de datos
  await db.tasks.create({ data: { title } });
  revalidatePath('/tasks');
}

Luego, en tu formulario cliente, llamas a esta acción con action={addTask}. Next.js maneja automáticamente la comunicación, enviando los datos del formulario al servidor, ejecutando la lógica y actualizando la UI con Streaming SSR para mostrar cambios en tiempo real sin recargar la página completa.

Caso de estudio

Vamos a aplicar esto a un sistema de inventario para una tienda online. Supongamos que tenemos una tabla de productos con estos datos:

IDNombrePrecioStock
1Laptop120010
2Mouse2550

Creamos Server Actions en app/inventory/actions.js para manejar operaciones CRUD:

  • createProduct: Toma datos de un formulario y los inserta en la base de datos.
  • getProducts: Recupera la lista de productos para mostrar en una tabla.
  • updateProduct: Actualiza el precio o stock basado en ID.
  • deleteProduct: Elimina un producto por ID, con validación de stock cero.

En la UI, usamos un formulario con action={createProduct} y una tabla que se actualiza dinámicamente gracias a revalidatePath. Esto permite al empleado de la tienda gestionar inventario en tiempo real, con feedback inmediato sobre errores como stock negativo.

Recuerda: Server Actions no reemplazan APIs públicas para integraciones externas, pero son ideales para operaciones internas donde controlas tanto el cliente como el servidor.

Errores comunes

  1. Olvidar 'use server': Si no incluyes esta directiva al inicio de tu función, Next.js intentará ejecutarla en el cliente, causando errores de seguridad o rendimiento. Siempre verifica que tus Server Actions estén marcadas correctamente.
  2. No validar datos de entrada: Confiar en datos del cliente sin sanitización puede llevar a inyecciones SQL o corrupción de datos. Usa bibliotecas como Zod para validar el formData antes de procesarlo.
  3. Ignorar revalidación de caché: Después de mutar datos (como crear o actualizar), olvidar llamar a revalidatePath o revalidateTag hace que la UI muestre información obsoleta. Inclúyelo siempre en tus acciones para mantener la coherencia.
  4. Usar Server Actions para cargas pesadas: Estas acciones son para operaciones rápidas; tareas como procesamiento de imágenes grandes deben delegarse a jobs en segundo plano para no bloquear respuestas.
  5. Exponer lógica sensible en errores Mostrar mensajes de error detallados puede filtrar información de la base de datos. Envía errores genéricos al cliente y registra los detalles en el servidor.

Checklist de dominio

  • Puedo crear una Server Action que maneje la creación de un recurso en una base de datos.
  • Sé cómo estructurar acciones para lectura, actualización y eliminación en un solo archivo.
  • Implemento validación de datos en el servidor antes de cualquier operación CRUD.
  • Uso revalidatePath o revalidateTag para actualizar la UI después de mutaciones.
  • Diferencio cuándo usar Server Actions (operaciones internas) vs APIs públicas (integraciones externas).
  • Manejo errores de forma segura, sin exponer detalles internos al cliente.
  • Optimizo el rendimiento evitando acciones bloqueantes en flujos críticos.

Construye un CRUD de Usuarios con Server Actions

En este ejercicio, crearás un sistema de gestión de usuarios para una app interna. Sigue estos pasos:

  1. Configura un proyecto Next.js 15 con App Router y una base de datos SQLite usando Prisma.
  2. Define un modelo de Usuario en prisma/schema.prisma con campos: id, nombre, email, y fecha de creación.
  3. Crea un archivo app/users/actions.js con Server Actions para:
    • createUser: Acepta nombre y email desde un formulario, valida que el email sea único, y guarda en la base de datos.
    • getUsers: Recupera todos los usuarios ordenados por fecha de creación.
    • updateUser: Permite actualizar el nombre de un usuario por ID.
    • deleteUser: Elimina un usuario por ID, solo si no tiene registros asociados (simula con una validación básica).
  4. Implementa una página en app/users/page.js que:
    • Muestre una lista de usuarios en una tabla HTML, usando getUsers.
    • Incluya un formulario para agregar nuevos usuarios, con action={createUser}.
    • Añada botones para editar y eliminar usuarios, que llamen a las acciones correspondientes.
  5. Prueba el flujo completo: agrega un usuario, edita su nombre, y elimínalo, verificando que la UI se actualice sin recargar.
Pistas
  • Usa 'use server' al inicio de cada función en actions.js para asegurar ejecución en el servidor.
  • Incluye revalidatePath('/users') en tus acciones de creación, actualización y eliminación para refrescar la lista automáticamente.
  • Para validar el email único, consulta la base de datos en createUser antes de insertar, y devuelve un error amigable si ya existe.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.

Sigue con Next.js 15: Aplicaciones Full-Stack con Server Components y Streaming SSR

Esta lección forma parte de un curso de Cursalo. Revisa su programa y elige entre la compra individual o el acceso a toda la escuela.

Si ya tenías acceso a este curso, entra con tu cuenta: tus derechos y tu progreso se conservan. No necesitas comprarlo de nuevo.

01 / ELEGIR UNA RUTA

El próximo paso

Aprende IA mientras construyes algo que sí necesitas.

Empieza por el curso inicial o explora el catálogo para encontrar una práctica alineada con tu trabajo.