Optimizar Consultas con DataLoader y Caching

Lectura
20 min~6 min lectura
Objetivo de la lección

DataLoader es una utilidad creada por Facebook que soluciona este problema mediante el batching y caching .

Puntos de control
  • Concepto clave
  • Como funciona en la practica
  • Caso de estudio
  • Errores comunes

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.

Laboratorio de práctica

Antes de marcar esta lección como completa, escribí una evidencia breve para GraphQL Profesional con Next.js y Apollo Server: APIs Tipadas y Suscripciones: un ejemplo, una decisión, una captura, una mini demo o una nota que puedas reutilizar en portfolio.

Reflexión rápida

¿Qué cambiarías en tu forma de trabajar después de aplicar optimizar consultas con dataloader y caching?

De lección a portfolio

Convertí esta lección en una prueba técnica visible.

Una app pequeña publicada, con README y decisiones explicadas, funciona mejor que una lista de tecnologías sueltas.

Paso 1

Creá una demo mínima que use el concepto de la lección.

Paso 2

Escribí un README corto con objetivo, stack, decisión técnica y mejora futura.

Paso 3

Publicá la demo y enlazala desde tu perfil profesional.

Newsletter Cursalo

Recibí rutas y cursos nuevos

Sumate para recibir recursos orientados a empleo y portfolio.

  • Rutas de empleo
  • Cursos prácticos
  • Portfolio y entrevistas

Sin spam. También podés entrar con tu cuenta para guardar progreso. Iniciá sesión

Optimizar Consultas con DataLoader y Caching | CursaloFalar no WhatsApp