Lección 6 de 20 · Lectura y práctica
Implementar Mutaciones con Validación de Datos
Concepto claveLas mutaciones en GraphQL son operaciones que modifican datos en el servidor, a diferencia de las consultas que solo los leen. En APIs profesionales, las mutaciones deben incluir validación de datos para garantizar.
Del concepto a la práctica
Taller de esta lección
Una mutación segura valida antes de cambiar el estado y debe distinguir entrada inválida de recurso inexistente. Trabajar con un input pequeño hace visible el orden: normalizar, comprobar reglas, localizar duplicados y solo entonces insertar.
Ejemplo resuelto
Para registrar una etiqueta:
input CreateTagInput { name: String!, color: String! }Acepta name: "backend" y color: "#204060"; rechaza nombre vacío, color sin seis dígitos hexadecimales o nombre repetido. La colección inicial contiene frontend y graphql.
Recorta espacios del nombre.
Comprueba longitud y patrón del color.
Busca coincidencia sin distinguir mayúsculas.
Inserta solo después de superar todas las reglas.
Error frecuente: insertar primero y validar después, dejando un registro parcial. Corrección: ejecuta todas las comprobaciones antes de modificar la colección.
Pruébalo
Intenta crear GraphQL con color #123456 y luego graphql con #ffffff.
Comprueba tu resultado
Ambas entradas se rechazan como duplicadas porque graphql ya estaba en la colección inicial; el número de registros permanece en dos.
Cuenta las inserciones con el fixture real
Después prueba backend con #204060: supera la validación y aumenta la colección de 2 a 3. Repetir Backend con otra capitalización se rechaza y conserva 3. El input String! comprueba tipo y nulabilidad, pero permite una cadena vacía: el servicio debe imponer longitud y patrón del color. En una base compartida, aplica además una restricción única sobre el nombre normalizado; una lectura previa puede competir con otra inserción. Nunca dejes que un error posterior produzca una etiqueta incompleta.

Mapa de prácticaEl navegador envía una solicitud a la API. El servidor valida la operación y consulta los datos; después devuelve una respuesta que el navegador puede mostrar. La base de datos se comunica con el servidor y no directamente con el navegador.
Ver la infografía en tamaño completo ↗Fuentes para profundizar
Concepto clave
Las mutaciones en GraphQL son operaciones que modifican datos en el servidor, a diferencia de las consultas que solo los leen. En APIs profesionales, las mutaciones deben incluir validación de datos para garantizar que la información recibida cumple con las reglas de negocio antes de procesarla. Esto es similar a un formulario de registro en línea: el sistema verifica que el correo electrónico tenga formato válido y la contraseña cumpla requisitos de seguridad antes de crear la cuenta.
En el contexto de GraphQL con TypeScript y Apollo Server, la validación se implementa típicamente en dos capas: primero en el esquema GraphQL usando tipos escalares personalizados y directivas, y luego en los resolvers mediante lógica de validación explícita. Esta doble capa protege contra datos malformados y ataques comunes como inyección de datos.
Cómo funciona en la práctica
Implementar una mutación con validación sigue estos pasos:
- Definir el tipo de entrada en el esquema GraphQL con tipos específicos
- Crear un resolver que reciba los argumentos validados
- Implementar lógica de validación antes de la operación de base de datos
- Devolver un tipo de respuesta estandarizado que incluya posibles errores
Ejemplo básico de esquema:
type Mutation {
crearUsuario(input: CrearUsuarioInput!): UsuarioResponse!
}
input CrearUsuarioInput {
email: String!
password: String!
edad: Int!
}
type UsuarioResponse {
success: Boolean!
message: String
usuario: Usuario
errors: [ValidationError!]
}
type ValidationError {
field: String!
message: String!
}En el resolver, validamos los datos:
const crearUsuario = async (_, { input }) => {
const errors = []
// Validación de email
if (!isValidEmail(input.email)) {
errors.push({ field: 'email', message: 'Email inválido' })
}
// Validación de contraseña
if (input.password.length < 8) {
errors.push({ field: 'password', message: 'La contraseña debe tener al menos 8 caracteres' })
}
// Validación de edad
if (input.edad < 18) {
errors.push({ field: 'edad', message: 'Debes ser mayor de 18 años' })
}
if (errors.length > 0) {
return {
success: false,
message: 'Errores de validación',
usuario: null,
errors
}
}
// Si pasa validación, crear usuario
const usuario = await db.usuarios.create(input)
return {
success: true,
message: 'Usuario creado exitosamente',
usuario,
errors: []
}
}Caso de estudio
Imagina que estás construyendo un sistema de reservas para un restaurante. Necesitas una mutación crearReserva que valide:
| Campo | Regla de validación | Mensaje de error |
|---|---|---|
| fecha | Debe ser futura (no pasada) | "La fecha debe ser futura" |
| personas | Entre 1 y 12 personas | "El número de personas debe estar entre 1 y 12" |
| telefono | Formato de teléfono válido | "Teléfono inválido" |
| Email válido y único en el sistema | "Email inválido o ya registrado" |
Implementación del resolver:
const crearReserva = async (_, { input }, context) => {
const { fecha, personas, telefono, email } = input
const errors = []
// Validar fecha futura
if (new Date(fecha) < new Date()) {
errors.push({ field: 'fecha', message: 'La fecha debe ser futura' })
}
// Validar número de personas
if (personas < 1 || personas > 12) {
errors.push({ field: 'personas', message: 'El número de personas debe estar entre 1 y 12' })
}
// Validar teléfono
if (!/^[+]?[\d\s-]{10,}$/.test(telefono)) {
errors.push({ field: 'telefono', message: 'Teléfono inválido' })
}
// Validar email único
const existeEmail = await db.reservas.findUnique({ where: { email } })
if (existeEmail) {
errors.push({ field: 'email', message: 'Email ya registrado para otra reserva' })
}
if (errors.length > 0) {
return {
success: false,
reserva: null,
errors
}
}
// Crear reserva
const reserva = await db.reservas.create({
data: { fecha, personas, telefono, email }
})
return {
success: true,
reserva,
errors: []
}
}La validación en el servidor es crucial: nunca confíes en la validación del cliente, ya que puede ser omitida o manipulada.
Errores comunes
- Validar solo en el cliente: Los atacantes pueden enviar peticiones directamente al servidor omitiendo la validación del frontend. Siempre valida en el servidor.
- Mensajes de error genéricos: Evita mensajes como "Error en los datos". Sé específico sobre qué campo falló y por qué.
- No validar unicidad: Olvidar verificar si un valor único (como email) ya existe en la base de datos.
- Validar después de operaciones costosas: Realiza validaciones antes de cualquier operación de base de datos o cálculo complejo.
- Falta de validación de tipos: Aunque GraphQL valida tipos básicos, necesitas validar reglas de negocio específicas.
Checklist de dominio
- Puedo definir un tipo de entrada (
input) en el esquema GraphQL con todos los campos necesarios - Sé implementar validación de formato (email, teléfono, URLs) en los resolvers
- Puedo validar reglas de negocio (rangos, unicidad, dependencias entre campos)
- Sé estructurar respuestas de error informativas para el cliente
- Puedo diferenciar entre errores de validación y errores del servidor
- Sé usar tipos escalares personalizados para validación a nivel de esquema
- Puedo testear mutaciones con datos válidos e inválidos
Implementar mutación para actualizar perfil de usuario con validación
En este ejercicio, implementarás una mutación actualizarPerfil que permita a los usuarios actualizar su información personal con validación robusta.
- Crea un tipo de entrada
ActualizarPerfilInputen tu esquema GraphQL con estos campos:nombre(String, opcional)email(String, opcional)fechaNacimiento(String, opcional)biografia(String, opcional)
- Implementa el resolver
actualizarPerfilen Apollo Server con estas validaciones:nombre: mínimo 2 caracteres, máximo 50 (si se proporciona)email: formato válido y único en el sistema (si se proporciona)fechaNacimiento: fecha válida y el usuario debe tener al menos 13 años (si se proporciona)biografia: máximo 500 caracteres (si se proporciona)
- Estructura la respuesta usando un tipo
ActualizarPerfilResponseque incluya:success(Boolean)message(String)usuario(Usuario)errors([ValidationError])
- Implementa lógica para actualizar solo los campos proporcionados (no sobrescribir con null)
- Testea tu mutación con al menos 3 casos: datos válidos, datos inválidos, y mezcla de válidos/inválidos
Pistas
- Usa operadores de propagación (...) para actualizar solo los campos proporcionados sin afectar los demás
- Para validar la edad mínima, calcula la diferencia entre la fecha actual y fechaNacimiento
- Considera usar una librería como validator.js para validaciones de formato complejas
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.
Sigue con GraphQL Profesional con Next.js y Apollo Server: APIs Tipadas y Suscripciones
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.