Crear Resolvers Simples para Consultas

Video
25 min~5 min lectura

Reproductor de video

Concepto clave

Los resolvers son funciones que se ejecutan para obtener los datos solicitados en una consulta GraphQL. Piensa en ellos como los "camareros" de tu API: cuando un cliente (frontend) pide un plato específico (campo de datos), el camarero va a la cocina (base de datos, servicio externo, etc.) y trae exactamente lo que se solicitó.

En GraphQL con Apollo Server, cada campo definido en tu schema necesita un resolver correspondiente. La magia está en que GraphQL resuelve los campos de forma jerárquica: si consultas usuario { nombre posts { titulo } }, primero se ejecuta el resolver para usuario, luego para nombre (que suele ser simple), y finalmente para posts (que puede requerir otra llamada a base de datos).

Un resolver recibe cuatro argumentos: parent (datos del nivel superior), args (argumentos de la consulta), context (información compartida como autenticación), y info (detalles de la consulta).

Cómo funciona en la práctica

Imagina que tienes un schema GraphQL para una aplicación de blog. Primero, defines los tipos en tu schema:

type Post {
  id: ID!
  titulo: String!
  contenido: String!
  autor: Usuario!
}

type Usuario {
  id: ID!
  nombre: String!
  email: String!
  posts: [Post!]!
}

type Query {
  posts: [Post!]!
  usuario(id: ID!): Usuario
}

Luego, implementas los resolvers. Aquí un ejemplo paso a paso:

  1. Crea un archivo resolvers.js con un objeto que mapee los tipos y queries.
  2. Para el query posts, define una función que devuelva datos (por ahora, datos simulados).
  3. Para el campo autor en Post, usa el argumento parent para acceder al ID del autor y buscar el usuario correspondiente.

Ejemplo de implementación:

const resolvers = {
  Query: {
    posts: () => {
      // Simula datos de una base de datos
      return [
        { id: '1', titulo: 'Aprendiendo GraphQL', contenido: '...', autorId: '101' },
        { id: '2', titulo: 'Next.js Tips', contenido: '...', autorId: '102' }
      ];
    },
    usuario: (parent, args, context, info) => {
      const { id } = args;
      // Busca usuario por ID (simulado)
      return { id, nombre: 'Carlos', email: '[email protected]' };
    }
  },
  Post: {
    autor: (parent) => {
      // parent contiene los datos del post, como autorId
      return { id: parent.autorId, nombre: 'Autor Simulado' };
    }
  }
};

Caso de estudio

Vamos a aplicar esto a un escenario real: una API para un e-commerce con productos y categorías. Supón que tienes este schema:

type Producto {
  id: ID!
  nombre: String!
  precio: Float!
  categoria: Categoria!
}

type Categoria {
  id: ID!
  nombre: String!
  productos: [Producto!]!
}

type Query {
  productos: [Producto!]!
  categoria(id: ID!): Categoria
}

Implementa resolvers que:

  • Para Query.productos, devuelva una lista de productos desde una fuente de datos (en este caso, un array en memoria).
  • Para Producto.categoria, use el parent para buscar la categoría correspondiente por ID.
  • Para Categoria.productos, filtre los productos por categoría ID.

Datos de ejemplo en una tabla:

ID ProductoNombrePrecioCategoria ID
P1Laptop999.99C1
P2Mouse25.50C2
P3Teclado45.00C2

El resolver de Producto.categoria podría verse así:

Producto: {
  categoria: (parent) => {
    const categorias = [
      { id: 'C1', nombre: 'Electrónica' },
      { id: 'C2', nombre: 'Accesorios' }
    ];
    return categorias.find(cat => cat.id === parent.categoriaId);
  }
}

Errores comunes

  • Olvidar devolver el tipo correcto: Si tu schema define String! y devuelves un número, Apollo Server lanzará un error. Siempre valida que los datos coincidan con el schema.
  • No manejar argumentos en resolvers: Al definir un query con argumentos (ej: usuario(id: ID!)), asegúrate de acceder a args.id en el resolver. Un error típico es usar parent.id por confusión.
  • Resolver asíncrono sin await: Si tu resolver hace una llamada a una base de datos (como con Prisma o MongoDB), recuerda usar async/await o promesas. Olvidarlo puede devolver Promise en lugar de los datos.
  • Ignorar el contexto: El context es ideal para compartir datos como conexiones a DB o información de autenticación. No inicializarlo puede llevar a código repetitivo o inseguro.
  • Sobrecargar resolvers con lógica compleja: Mantén los resolvers simples; delega la lógica de negocio a servicios o capas separadas para mejorar mantenibilidad.

Checklist de dominio

  1. Puedo explicar qué es un resolver y su rol en GraphQL usando una analogía simple.
  2. He implementado al menos un query con argumentos y su resolver correspondiente.
  3. Sé acceder y usar los cuatro argumentos (parent, args, context, info) en un resolver.
  4. Puedo conectar resolvers para campos anidados (ej: usuario.posts).
  5. He simulado datos en memoria y los he devuelto desde un resolver.
  6. Reconozco y evito errores comunes como tipos incorrectos o manejo asíncrono.
  7. Puedo integrar un resolver básico en un proyecto Next.js con Apollo Server.

Implementa Resolvers para una API de Biblioteca

En este ejercicio, crearás resolvers simples para una API GraphQL que gestiona libros y autores en una biblioteca. Sigue estos pasos:

  1. Configura un proyecto Next.js con Apollo Server (usa el boilerplate del módulo anterior).
  2. Define el siguiente schema GraphQL en tu archivo schema.js:
    type Libro {
      id: ID!
      titulo: String!
      autor: Autor!
      año: Int!
    }
    
    type Autor {
      id: ID!
      nombre: String!
      libros: [Libro!]!
    }
    
    type Query {
      libros: [Libro!]!
      autor(id: ID!): Autor
    }
  3. Crea un archivo resolvers.js e implementa los resolvers para:
    • Query.libros: Devuelve un array con al menos 3 libros simulados (ej: datos en memoria).
    • Query.autor: Acepta un argumento id y devuelve un autor simulado basado en ese ID.
    • Libro.autor: Usa el parent para buscar y devolver el autor correspondiente al libro.
    • Autor.libros: Filtra y devuelve los libros escritos por ese autor.
  4. Integra los resolvers en tu Apollo Server y prueba las consultas en GraphQL Playground (ej: { libros { titulo autor { nombre } } }).
  5. Asegúrate de que los tipos devueltos coincidan con el schema y maneja los argumentos correctamente.

Datos de ejemplo para empezar:

  • Libros: [{ id: 'L1', titulo: 'Cien años de soledad', autorId: 'A1', año: 1967 }, { id: 'L2', titulo: 'Rayuela', autorId: 'A2', año: 1963 }]
  • Autores: [{ id: 'A1', nombre: 'Gabriel García Márquez' }, { id: 'A2', nombre: 'Julio Cortázar' }]
Pistas
  • Usa el argumento 'parent' en el resolver de Libro.autor para acceder a autorId y buscar en el array de autores.
  • En Query.autor, el argumento 'id' está en args, no en parent. Asegúrate de extraerlo con const { id } = args.
  • Para Autor.libros, puedes filtrar el array de libros comparando autorId con el id del autor desde parent.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.