En el desarrollo web, optimizar y escalar aplicaciones siempre ha sido un problema. React.js ha tenido un éxito extraordinario en el desarrollo de frontend como una herramienta que proporciona una forma sólida de crear interfaces de usuario. Pero se complica con aplicaciones en crecimiento, especialmente cuando se trata de múltiples puntos finales de API REST. Preocupaciones como el exceso de recuperación, donde se requiere una cantidad excesiva de datos, pueden ser una fuente de cuellos de botella de rendimiento y una mala experiencia de usuario.
Entre las soluciones a estos desafíos está adoptar el uso de GraphQL con aplicaciones React. Si su backend tiene múltiples puntos finales REST, entonces introducir una capa GraphQL que llame internamente a sus puntos finales de API REST puede mejorar su aplicación evitando el exceso de recuperación y optimizar su aplicación frontend. En este artículo, encontrará cómo utilizarlo, las ventajas y desventajas de este enfoque, varios desafíos y cómo abordarlos. También exploraremos algunos ejemplos prácticos de cómo GraphQL puede ayudarlo a mejorar la forma en que trabaja con sus datos.
Exceso de recuperación en las API REST
En las API REST, el exceso de recuperación ocurre cuando la cantidad de datos que la API entrega al cliente es mayor de lo que el cliente requiere. Este es un problema común con las API REST, que a menudo devuelven un Esquema de Objeto o Respuesta fijo. Para comprender mejor este problema, consideremos un ejemplo.
Considere una página de perfil de usuario donde solo es necesario mostrar el nombre
y el correo electrónico
del usuario. Con una API REST típica, la obtención de los datos del usuario podría verse así:
fetch('/api/users/1')
.then(response => response.json())
.then(user => {
// Use the user's name and profilePicture in the UI
});
La respuesta de la API incluirá datos innecesarios:
{
"id": 1,
"name": "John Doe",
"profilePicture": "/images/john.jpg",
"email": "[email protected]",
"address": "123 Denver St",
"phone": "111-555-1234",
"preferences": {
"newsletter": true,
"notifications": true
},
// ...more details
}
Aunque la aplicación solo requiere los campos de nombre y correo electrónico del usuario, la API devuelve todo el objeto del usuario. Estos datos adicionales a menudo aumentan el tamaño de la carga útil, consumen más ancho de banda y pueden eventualmente ralentizar la aplicación cuando se utiliza en un dispositivo con recursos limitados o una conexión de red lenta.
GraphQL como Solución
GraphQL aborda el problema de la sobrecarga de datos permitiendo que los clientes soliciten exactamente los datos que necesitan. Al integrar un servidor GraphQL en tu aplicación, puedes crear una capa de recuperación de datos flexible y eficiente que se comunique con tus APIs REST existentes.
Cómo Funciona
1. Configuración del Servidor GraphQL
Introduces un servidor GraphQL que actúa como intermediario entre tu frontend de React y las APIs REST.
2. Definición del Esquema
Defines un esquema GraphQL que especifica los tipos de datos y consultas que tu frontend requiere.
3. Implementación de Resolvers
Implementas resolvers en el servidor GraphQL que recuperan datos de las APIs REST y devuelven solo los campos necesarios.
4. Integración en el Front-End
Actualizas tu aplicación de React para usar consultas GraphQL en lugar de llamadas directas a la API REST.
Este enfoque te permite optimizar la recuperación de datos sin rehacer tu infraestructura de backend existente.
Implementando GraphQL en una Aplicación de React
Veamos cómo configurar un servidor GraphQL e integrarlo en una aplicación React.
Instalar Dependencias
npm install apollo-server graphql axios
Definir el Esquema
Crea un archivo llamado schema.js
:
const { gql } = require('apollo-server');
const typeDefs = gql`
type User {
id: ID!
name: String
email: String // Ensure this matches exactly with the frontend query
}
type Query {
user(id: ID!): User
}
`;
module.exports = typeDefs;
Este esquema define un tipo User
y una consulta user
que obtiene un usuario por ID.
Implementar Resolutores
Crea un archivo llamado resolvers.js
:
const resolvers = {
Query: {
user: async (_, { id }) => {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
const user = await response.json();
return {
id: user.id,
name: user.name,
email: user.email, // Return email instead of profilePicture
};
} catch (error) {
throw new Error(`Failed to fetch user: ${error.message}`);
}
},
},
};
module.exports = resolvers;
El resolutor para la consulta user
obtiene datos de la API REST y devuelve solo los campos requeridos.
Usaremos https://jsonplaceholder.typicode.com/ para nuestra API REST falsa.
Configurar el Servidor
Crea un archivo server.js
:
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');
const server = new ApolloServer({
typeDefs,
resolvers,
});
server.listen({ port: 4000 }).then(({ url }) => {
console.log(`GraphQL Server ready at ${url}`);
});
Inicia el servidor:
node server.js
Tu servidor GraphQL está activo en http://localhost:4000/graphql, y si consultas tu servidor, te llevará a esta página.
Integrando Con la Aplicación React
Ahora cambiaremos la aplicación React para usar la API GraphQL.
Instalar Apollo Client
npm install @apollo/client graphql
Configurar Apollo Client
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:4000',
cache: new InMemoryCache(),
});
Escribir la Consulta GraphQL
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
Ahora, integra las piezas de código anteriores con tu aplicación React. Aquí hay una simple aplicación react a continuación, que permite a un usuario seleccionar el userId y mostrar la información:
import { useState } from 'react';
import { ApolloClient, InMemoryCache, ApolloProvider, gql, useQuery } from '@apollo/client';
import './App.css'; // Link to the updated CSS
const client = new ApolloClient({
uri: 'http://localhost:4000', // Ensure this is the correct URL for your GraphQL server
cache: new InMemoryCache(),
});
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
const User = ({ userId }) => {
const { loading, error, data } = useQuery(GET_USER, {
variables: { id: userId },
});
if (loading) return <p>Loading</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div className="user-container">
<h2>{data.user.name}</h2>
<p>Email: {data.user.email}</p>
</div>
);
};
const App = () => {
const [selectedUserId, setSelectedUserId] = useState("1");
return (
<ApolloProvider client={client}>
<div className="app-container">
<h1 className="title">GraphQL User Lookup</h1>
<div className="dropdown-container">
<label htmlFor="userSelect">Select User ID:</label>
<select
id="userSelect"
value={selectedUserId}
onChange={(e) => setSelectedUserId(e.target.value)}
>
{Array.from({ length: 10 }, (_, index) => (
<option key={index + 1} value={index + 1}>
{index + 1}
</option>
))}
</select>
</div>
<User userId={selectedUserId} />
</div>
</ApolloProvider>
);
};
export default App;
Resultado
Verás detalles simples del usuario como este: [Enlace de Github].
Trabajando con Múltiples Puntos finales
Imagina un escenario en el que necesitas recuperar las publicaciones de un usuario específico, junto con los comentarios individuales en cada publicación. En lugar de hacer tres llamadas API separadas desde tu aplicación frontend de React y lidiar con datos innecesarios, puedes optimizar el proceso con GraphQL. Al definir un esquema y crear una consulta GraphQL, puedes solicitar solo los datos exactos que tu interfaz de usuario requiere, lo cual es una solicitud eficiente en una sola.
Necesitamos obtener datos de usuario, sus publicaciones y comentarios para cada publicación desde diferentes puntos finales. Utilizaremos fetch para recopilar datos de los múltiples puntos finales y devolverlos a través de GraphQL.
Actualizar Resolutores
const fetch = require('node-fetch');
const resolvers = {
Query: {
user: async (_, { id }) => {
try {
// fetch user
const userResponse = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
const user = await userResponse.json();
// fetch posts for a user
const postsResponse = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${id}`);
const posts = await postsResponse.json();
// fetch comments for a post
const postsWithComments = await Promise.all(
posts.map(async (post) => {
const commentsResponse = await fetch(`https://jsonplaceholder.typicode.com/comments?postId=${post.id}`);
const comments = await commentsResponse.json();
return { post, comments };
})
);
return {
id: user.id,
name: user.name,
email: user.email,
posts: postsWithComments,
};
} catch (error) {
throw new Error(`Failed to fetch user data: ${error.message}`);
}
},
},
};
module.exports = resolvers;
Actualizar Esquema de GraphQL
const { gql } = require('apollo-server');
const typeDefs = gql`
type Comment {
id: ID!
name: String
email: String
body: String
}
type Post {
id: ID!
title: String
body: String
comments: [Comment]
}
type User {
id: ID!
name: String
email: String
posts: [Post]
}
type Query {
user(id: ID!): User
}
`;
module.exports = typeDefs;
La configuración del servidor en server.js
sigue siendo la misma. Una vez que actualizamos el código de React.js, obtenemos la siguiente salida:
Resultado
Verás un usuario detallado como este: [Enlace de Github].
Beneficios de Este Enfoque
Integrar GraphQL en tu aplicación React proporciona varias ventajas:
Eliminación de Exceso de Datos
Una característica clave de GraphQL es que solo obtiene exactamente lo que solicitas. El servidor solo devuelve los campos solicitados y asegura que la cantidad de datos transferidos a través de la red se reduzca al servir solo lo que la consulta demanda, mejorando así el rendimiento.
Simplificación del Código Front-End
GraphQL te permite obtener la información necesaria en una sola consulta, independientemente de su origen. Internamente, podría estar haciendo 3 llamadas a la API para obtener la información. Esto ayuda a simplificar tu código frontend porque ahora no necesitas orquestar diferentes solicitudes asíncronas y combinar sus resultados.
Mejorando la Experiencia del Desarrollador
Un fuerte tipado y la introspección del esquema ofrecen mejores herramientas y verificación de errores que en la implementación tradicional de API. Además, existen entornos interactivos donde los desarrolladores pueden construir y probar consultas, incluyendo GraphiQL o Apollo Explorer.
Abordando Complejidades y Desafíos
Este enfoque tiene algunas ventajas, pero también introduce algunos desafíos que deben ser gestionados.
Capa Adicional en el Backend
La introducción del servidor GraphQL crea una capa extra en tu arquitectura de backend, y si no se gestiona adecuadamente, se convierte en un único punto de falla.
Solución
Presta atención al manejo de errores y monitoreo. Las herramientas de contenedorización y orquestación como Docker y Kubernetes pueden ayudar a gestionar la escalabilidad y la fiabilidad.
Potencial Sobrecarga de Rendimiento
El servidor GraphQL puede hacer múltiples llamadas a la API REST para resolver una sola consulta, lo que puede introducir latencia y sobrecarga en el sistema.
Solución
Cachea los resultados para evitar hacer varias llamadas a la API. Algunas herramientas, como DataLoader, pueden gestionar el proceso de agrupación y almacenamiento en caché de solicitudes.
Conclusión
“La simplicidad es la máxima sofisticación” — Leonardo da Vinci
Integrar GraphQL en tu aplicación de React es más que solo una optimización de rendimiento, es un movimiento estratégico hacia la construcción de aplicaciones más mantenibles, escalables y eficientes. Al abordar el sobreconsumo de datos y simplificar la gestión de datos, no solo mejoras la experiencia del usuario, sino que también capacitas a tu equipo de desarrollo con mejores herramientas y prácticas.
Aunque la introducción de una capa de GraphQL viene con su propio conjunto de desafíos, los beneficios a menudo superan las complejidades. Al planificar cuidadosamente tu implementación, optimizar tus resolutores y asegurar tus puntos finales, puedes mitigar posibles inconvenientes. Además, la flexibilidad que ofrece GraphQL puede hacer que tu aplicación sea más resistente a futuro a medida que crece y evoluciona.
Adoptar GraphQL no significa abandonar tus APIs REST existentes. En cambio, te permite aprovechar sus fortalezas al mismo tiempo que proporciona una capa de acceso a datos más eficiente y flexible para tus aplicaciones front-end. Este enfoque híbrido combina la confiabilidad de REST con la agilidad de GraphQL, brindándote lo mejor de ambos mundos.
Si estás listo para llevar tu aplicación de React al siguiente nivel, considera integrar GraphQL en tu estrategia de obtención de datos. El camino puede presentar desafíos, pero las recompensas — un proceso de desarrollo más fluido, desarrolladores más felices y usuarios satisfechos — hacen que valga la pena el esfuerzo.
Código completo disponible
Puedes encontrar el código completo de esta implementación en mi repositorio de GitHub.
Source:
https://dzone.com/articles/enhancing-react-applications-with-graphql-over-rest-apis