Lección 8 de 20 · Lectura y práctica
Crear Resolvers Anidados para Relaciones
Concepto claveLos resolvers anidados son funciones que permiten resolver campos relacionados en GraphQL cuando una consulta solicita datos de múltiples niveles. Imagina que tienes una API para una biblioteca: cuando consultas un.
Del concepto a la práctica
Taller de esta lección
Un resolver anidado convierte una clave de relación en un objeto relacionado sin duplicar datos en el registro principal. Para una biblioteca, Book.categoryId basta como almacenamiento; el campo category puede resolver la categoría bajo demanda.
Ejemplo resuelto
Libros: b1 y b2 tienen categoryId: "c1"; b3 tiene "c2". Categorías: c1 Ciencia, c2 Historia. El resolver Book.category(parent) busca por parent.categoryId, no por un argumento que el cliente no controla.
Define
Bookconcategory: Category!.Conserva
categoryIdcomo clave interna no expuesta si no es necesaria.Implementa el resolver anidado usando el
parentrecibido.Consulta dos libros y selecciona
titleycategory.name.
Error frecuente: devolver siempre la primera categoría. Corrección: usa la clave del parent y comprueba que cada relación apunte a un registro válido.
Pruébalo
Añade b4 con categoryId: "c2" y solicita los cuatro libros.
Comprueba tu resultado
b1 y b2 muestran Ciencia; b3 y b4, Historia. Ningún libro recibe una categoría basada en su posición del array.
Integridad y ausencia de categoría
Declara todos los libros y ambas categorías en el fixture antes de ejecutar. Con category:Category!, una categoryId rota no puede convertirse en un objeto vacío válido: null genera un error de ejecución que se propaga según la nulabilidad de Book y su lista. Mantén una clave foránea o valida el vínculo al escribir; si el dominio acepta libros sin categoría, cambia el contrato a Category nullable y prueba explícitamente ese caso. La carga bajo demanda se puede agrupar con un DataLoader nuevo por solicitud.

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
Los resolvers anidados son funciones que permiten resolver campos relacionados en GraphQL cuando una consulta solicita datos de múltiples niveles. Imagina que tienes una API para una biblioteca: cuando consultas un libro, también quieres información sobre su autor. En lugar de hacer dos consultas separadas, GraphQL te permite anidar estos datos en una sola petición.
En términos técnicos, cada campo en un tipo GraphQL puede tener su propio resolver. Cuando un campo representa una relación (como author en un tipo Book), necesitas un resolver anidado que obtenga esos datos relacionados. Esto es fundamental para construir APIs eficientes y tipadas, ya que evita el over-fetching (obtener datos innecesarios) y permite estructuras de datos complejas.
Cómo funciona en la práctica
Supongamos que tienes un esquema GraphQL con tipos Book y Author. El tipo Book tiene un campo author que referencia un Author. Para implementar esto en Apollo Server con TypeScript:
type Book {
id: ID!
title: String!
author: Author!
}
type Author {
id: ID!
name: String!
books: [Book!]!
}Los resolvers se estructuran así:
const resolvers = {
Book: {
author: async (parent, args, context) => {
// parent contiene los datos del libro actual
return await context.dataSources.authors.getById(parent.authorId);
}
},
Author: {
books: async (parent, args, context) => {
return await context.dataSources.books.getByAuthorId(parent.id);
}
}
};El parent en el resolver de author es el objeto Book actual, que típicamente incluye un authorId para buscar el autor relacionado. Apollo Server ejecuta estos resolvers automáticamente cuando se solicita el campo anidado.
Caso de estudio
Vamos a aplicar esto a un sistema de e-commerce con Next.js y Apollo Server. Tenemos tipos para Order y Product, donde cada orden contiene productos. El esquema:
type Order {
id: ID!
date: String!
products: [Product!]!
}
type Product {
id: ID!
name: String!
price: Float!
}En la base de datos, las órdenes y productos se almacenan en tablas separadas, con una tabla de unión order_products. El resolver para products en Order:
const resolvers = {
Order: {
products: async (parent, args, context) => {
// parent.id es el ID de la orden
const productIds = await context.db.orderProducts.findMany({
where: { orderId: parent.id },
select: { productId: true }
});
return await context.db.products.findMany({
where: { id: { in: productIds.map(p => p.productId) } }
});
}
}
};Este enfoque permite consultas como:
query {
order(id: "123") {
date
products {
name
price
}
}
}Obteniendo todos los datos en una sola petición, optimizando el rendimiento.
Errores comunes
- Resolver infinito: Si
Booktiene un campoauthoryAuthortiene un campobooks, y ambos se resuelven mutuamente sin límite, puede causar un bucle infinito. Solución: Limitar la profundidad de la consulta o usardataloaderpara caching. - N+1 queries: En el caso de estudio, si consultas 10 órdenes, el resolver de
productsse ejecuta 10 veces, generando muchas consultas a la base de datos. Evítalo condataloaderpara agrupar peticiones. - Tipado incorrecto: No definir bien los tipos en TypeScript puede llevar a errores en tiempo de ejecución. Asegúrate de que los resolvers devuelvan el tipo exacto esperado por el esquema.
- Olvidar el contexto: No pasar el
contextcorrectamente en Apollo Server puede hacer que los resolvers no accedan a fuentes de datos. Verifica la configuración del servidor.
Checklist de dominio
- Entiendo cómo los resolvers anidados mapean campos GraphQL a funciones en Apollo Server.
- Puedo implementar un resolver anidado para una relación uno-a-muchos (ej: autor-libros).
- Sé usar el parámetro
parentpara acceder a datos del objeto actual en el resolver. - Reconozco y evito problemas de N+1 queries usando técnicas como
dataloader. - Verifico que los tipos TypeScript en los resolvers coincidan con el esquema GraphQL.
- Puedo debuggear resolvers anidados usando herramientas como Apollo Studio.
- Aplico esto en un proyecto real con Next.js para construir APIs tipadas.
Implementar Resolvers Anidados para un Blog
En este ejercicio, crearás resolvers anidados para un sistema de blog usando Next.js y Apollo Server. El blog tiene tipos Post y Comment, donde cada post puede tener múltiples comentarios.
- Define un esquema GraphQL en un archivo
schema.tscon tiposPost(campos:id,title,content,comments) yComment(campos:id,text,postId). Asegúrate de quePost.commentssea una lista deComment. - Configura Apollo Server en un archivo
pages/api/graphql.tsen Next.js, incluyendo los tipos definidos. - Implementa resolvers en un archivo
resolvers.ts. Crea un resolver anidado para el campocommentsenPostque, dado eliddel post (disponible enparent.id), busque los comentarios relacionados desde una fuente de datos simulada (puedes usar un array en memoria). - Escribe una consulta GraphQL en Apollo Studio o un cliente para probar que, al solicitar un post, también obtienes sus comentarios anidados.
- Tipa los resolvers con TypeScript, asegurando que los tipos de retorno coincidan con el esquema.
Pistas
- Usa el parámetro parent en el resolver para acceder al id del post actual.
- Simula la fuente de datos con un array de objetos que incluya postId en los comentarios.
- Verifica que la consulta GraphQL solicite explícitamente el campo comments dentro de post.
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.