GraphQL Profesional con Next.js y Apollo Server: APIs Tipadas y Suscripciones

Optimizar Consultas con DataLoader y Caching

Concepto claveEn GraphQL, cada campo de un tipo puede tener su propio resolver, lo que significa que una sola consulta puede desencadenar multiples llamadas a la base de datos o APIs externas. Esto se conoce como el problema N+1: para una lista de N elementos, haces 1 consulta para obtener la lista y luego N consultas mas para obtener datos relacionados de cada elemento.DataLoader es una utilidad creada por Facebook que soluciona este problema mediante el batching y caching. Imagina que eres un
Tiempo de estudio
20 Min

Concepto clave

En GraphQL, cada campo de un tipo puede tener su propio resolver, lo que significa que una sola consulta puede desencadenar multiples llamadas a la base de datos o APIs externas. Esto se conoce como el problema N+1: para una lista de N elementos, haces 1 consulta para obtener la lista y luego N consultas mas para obtener datos relacionados de cada elemento.

DataLoader es una utilidad creada por Facebook que soluciona este problema mediante el batching y caching. Imagina que eres un camarero en un restaurante: en lugar de ir a la cocina por cada pedido individual (N+1), tomas todos los pedidos de la mesa (batching) y los llevas juntos a la cocina. Luego, si alguien pide lo mismo otra vez, ya lo tienes en tu bandeja (caching) y no necesitas volver a la cocina.

El caching en GraphQL va mas alla de DataLoader. Apollo Server incluye un cache integrado que puede almacenar respuestas completas o parciales, reduciendo la carga en tus resolvers. Combinar DataLoader para optimizar consultas a la base de datos con el cache de Apollo para respuestas HTTP es como tener un sistema de entrega express: agrupas paquetes similares y guardas los frecuentes para entregas instantaneas.

Como funciona en la practica

Vamos a implementar DataLoader en un resolver de GraphQL con Apollo Server. Supongamos que tenemos un tipo User y un tipo Post, donde cada post tiene un autor (usuario). Sin DataLoader, una consulta para obtener posts con sus autores generaria una consulta a la base de datos por cada autor.

Paso 1: Instala DataLoader en tu proyecto de Next.js con Apollo Server:

npm install dataloader

Paso 2: Crea un DataLoader para usuarios. En tu archivo de resolvers, define una funcion que cargue usuarios por sus IDs en batch:

const DataLoader = require('dataloader');

const batchUsers = async (userIds) => {
// Supongamos que tenemos una funcion getUsersByIds que acepta un array de IDs
const users = await getUsersByIds(userIds);
// DataLoader espera que devuelvas los usuarios en el mismo orden que los IDs
return userIds.map(id => users.find(user => user.id === id));
};

const userLoader = new DataLoader(batchUsers);

Paso 3: Usa el DataLoader en tu resolver. En el resolver para el campo author de Post, en lugar de hacer una consulta individual, usa el loader:

Post: {
author: async (post) => {
return userLoader.load(post.authorId);
}
}

Paso 4: Configura el cache de Apollo Server. En tu configuracion de Apollo Server, puedes habilitar el cache por defecto o personalizarlo:

const server = new ApolloServer({
typeDefs,
resolvers,
cache: new InMemoryCache(),
// Otras configuraciones...
});

Con esto, si una consulta pide multiples posts del mismo autor, DataLoader agrupara las llamadas y el cache de Apollo podra reutilizar respuestas si son identicas.

Caso de estudio

Imagina que estas construyendo una API para una plataforma de blogs con Next.js y Apollo Server. Tienes los siguientes tipos GraphQL:

type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}

type Post {
id: ID!
title: String!
content: String!
author: User!
comments: [Comment!]!
}

type Comment {
id: ID!
text: String!
user: User!
}

Una consulta tipica podria ser:

query {
posts {
id
title
author {
id
name
}
comments {
id
text
user {
id
name
}
}
}
}

Sin optimizacion, esto generaria:

  • 1 consulta para obtener todos los posts
  • N consultas para los autores de cada post (donde N es el numero de posts)
  • M consultas para los usuarios de cada comentario (donde M es el numero total de comentarios)

Con DataLoader y caching:

  1. Crea un DataLoader para usuarios que cargue por IDs en batch.
  2. En los resolvers de author en Post y user en Comment, usa userLoader.load(id).
  3. Configura Apollo Server con InMemoryCache para cachear respuestas de consultas completas.

Resultado: En lugar de 1+N+M consultas, tendras 1 consulta batch para usuarios (agrupando todos los IDs unicos) y el cache reducira llamadas repetidas. Para 10 posts con 5 comentarios cada uno, pasarias de ~61 consultas a solo 2-3, mejorando el rendimiento significativamente.

Errores comunes

1. No mantener el orden en DataLoader: DataLoader requiere que el array devuelto por la funcion batch tenga el mismo orden que los IDs de entrada. Si no lo haces, asociaras datos incorrectos. Solucion: Usa un mapa o logica de busqueda que preserve el orden, como en el ejemplo del paso 2.

2. Olvidar limpiar el cache: En entornos de desarrollo, el cache puede almacenar datos obsoletos si no se invalida cuando los datos cambian. Solucion: Usa tecnicas como cache invalidation con versiones o TTL (Time To Live), o en Apollo, considera resetear el cache en mutaciones relevantes.

3. Sobrecargar el cache con datos grandes: Cachear respuestas muy grandes puede consumir mucha memoria. Solucion: Configura limites en InMemoryCache o usa estrategias de cache por fragmentos, cacheando solo campos frecuentes.

4. No usar batching en resolvers anidados: Si tienes resolvers complejos con multiples niveles, podrias perder la oportunidad de agrupar llamadas. Solucion: Disena tus DataLoaders para manejar diferentes tipos de datos y usalos consistentemente en toda la API.

5. Ignorar el contexto de ejecucion: En GraphQL con suscripciones, el cache puede no ser adecuado para datos en tiempo real. Solucion: Para suscripciones, considera deshabilitar el cache o usar cache por sesion, y combina con DataLoader para consultas batch.

Checklist de dominio

  • Identificar consultas N+1 en tu API GraphQL usando herramientas como Apollo Studio o logging.
  • Implementar al menos un DataLoader para un tipo de dato comun, como usuarios o productos.
  • Configurar Apollo Server con InMemoryCache y probar su efecto en consultas repetidas.
  • Escribir tests que verifiquen que DataLoader reduce el numero de llamadas a la base de datos.
  • Manejar escenarios donde el cache debe invalidarse, como despues de mutaciones.
  • Integrar DataLoader en resolvers anidados para optimizar consultas complejas.
  • Medir el rendimiento antes y despues de la optimizacion con metricas como tiempo de respuesta y uso de CPU.

Optimizar una API de E-commerce con DataLoader y Caching


En este ejercicio, optimizaras una API GraphQL para una tienda online usando DataLoader y el cache de Apollo Server. Sigue estos pasos:

  1. Configura el entorno: Crea un proyecto Next.js con Apollo Server. Define tipos GraphQL para Product (con id, nombre, precio) y Order (con id, productos como array de IDs de producto, usuarioId).
  2. Simula el problema N+1: Escribe resolvers basicos que, para una consulta de ordenes con productos, hagan una consulta a la base de datos por cada producto. Usa datos mock en memoria para simular la base de datos.
  3. Implementa DataLoader: Crea un DataLoader para productos que cargue por IDs en batch. Modifica el resolver de productos en Order para usar productLoader.load(id).
  4. Configura el cache: Habilitar InMemoryCache en Apollo Server. Escribe una consulta que pida multiples ordenes con los mismos productos y verifica que el cache reduce llamadas.
  5. Prueba y mide: Agrega logging para contar llamadas a la funcion batch. Ejecuta consultas antes y despues de la optimizacion, comparando el numero de llamadas y el tiempo de respuesta.

Pistas
  • Usa un objeto en memoria para simular la base de datos, por ejemplo, un array de productos con IDs unicos.
  • En la funcion batch de DataLoader, asegurate de devolver los productos en el mismo orden que los IDs de entrada.
  • Para probar el cache, ejecuta la misma consulta dos veces y verifica que la segunda sea mas rapida o tenga menos logs.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.

Texto Leccion 3/20
Estas viendo
Optimizar Consultas con DataLoader y Caching
Hablar por WhatsAppContactar por WhatsApp