В веб-разработке оптимизация и масштабирование приложений всегда были проблемой. React.js имел невероятный успех в разработке фронтенда как инструмент, предоставляющий надежный способ создания пользовательских интерфейсов. Но возникают сложности с ростом приложений, особенно когда речь идет о нескольких конечных точках множественных REST API. Проблемы, такие как избыточное получение данных, когда требуется избыточное количество данных, могут быть источником узких мест производительности и плохого пользовательского опыта.
Среди решений этих проблем является применение GraphQL в приложениях React. Если ваш бэкенд имеет несколько конечных точек REST, то внедрение слоя GraphQL, который внутренне вызывает ваши конечные точки REST API, может улучшить ваше приложение, избавив от избыточного получения данных и оптимизировав ваше приложение фронтенда. В этой статье вы узнаете, как его использовать, преимущества и недостатки такого подхода, различные проблемы и способы их решения. Мы также более детально рассмотрим некоторые практические примеры того, как GraphQL может помочь вам улучшить способы работы с вашими данными.
Избыточное получение данных в REST API
В REST API избыточное получение данных происходит, когда объем данных, который API передает клиенту, больше, чем тому требуется. Это распространенная проблема с REST API, которые часто возвращают фиксированный объект или схему ответа. Для лучшего понимания этой проблемы рассмотрим пример.
Представьте себе страницу профиля пользователя, где требуется показать только имя
и электронную почту
пользователя. С использованием типичного REST API получение данных пользователя может выглядеть так:
fetch('/api/users/1')
.then(response => response.json())
.then(user => {
// Use the user's name and profilePicture in the UI
});
Ответ API будет содержать ненужные данные:
{
"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
}
Хотя приложение требует только имя и электронную почту пользователя, API возвращает целый объект пользователя. Эти дополнительные данные часто увеличивают размер пакета, требуют больше пропускной способности и могут в конечном итоге замедлить работу приложения при использовании на устройстве с ограниченными ресурсами или медленным сетевым подключением.
GraphQL как решение
GraphQL решает проблему избыточной передачи данных, позволяя клиентам запрашивать ровно те данные, которые им нужны. Интегрируя сервер GraphQL в ваше приложение, вы можете создать гибкий и эффективный слой для извлечения данных, который взаимодействует с вашими существующими REST API.
Как это работает
1. Настройка сервера GraphQL
Вы вводите сервер GraphQL, который служит посредником между вашим фронтендом React и REST API.
2. Определение схемы
Вы определяете схему GraphQL, которая указывает типы данных и запросы, необходимые вашему фронтенду.
3. Реализация резолверов
Вы реализуете резолверы на сервере GraphQL, которые извлекают данные из REST API и возвращают только необходимые поля.
4. Интеграция с фронтендом
Вы обновляете свое приложение React для использования запросов GraphQL вместо прямых вызовов REST API.
Этот подход позволяет оптимизировать извлечение данных без переделывания существующей инфраструктуры бэкенда.
Реализация GraphQL в приложении React
Давайте рассмотрим, как настроить сервер GraphQL и интегрировать его в приложение React.
Установите зависимости
npm install apollo-server graphql axios
Определите схему
Создайте файл с именем 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;
Эта схема определяет тип User
и запрос user
, который получает пользователя по ID.
Реализуйте разрешители
Создайте файл с именем 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;
Разрешитель для запроса user
получает данные из REST API и возвращает только необходимые поля.
Мы будем использовать https://jsonplaceholder.typicode.com/ для нашего фейкового REST API.
Настройте сервер
Создайте файл 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}`);
});
Запустите сервер:
node server.js
Ваш сервер GraphQL доступен по адресу http://localhost:4000/graphql, и если вы выполните запрос к вашему серверу, он перенаправит вас на эту страницу.
Интеграция с приложением React
Теперь мы изменим приложение React, чтобы использовать API GraphQL.
Установите Apollo Client
npm install @apollo/client graphql
Настройте Apollo Client
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:4000',
cache: new InMemoryCache(),
});
Напишите запрос GraphQL
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
Теперь интегрируйте указанные выше части кода с вашим приложением React. Вот простое приложение React ниже, которое позволяет пользователю выбрать userId и отобразить информацию:
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;
Результат
Вы увидите простые данные пользователя, такие как: [Ссылка на Github].
Работа с несколькими конечными точками
Представьте себе сценарий, в котором вам необходимо получить посты конкретного пользователя вместе с отдельными комментариями к каждому посту. Вместо того чтобы выполнять три отдельных вызова API из вашего фронтенд-приложения на React и иметь дело с ненужными данными, вы можете оптимизировать процесс с помощью GraphQL. Определив схему и составив запрос GraphQL, вы можете запросить только те данные, которые необходимы вашему интерфейсу, что является эффективным запросом в одном.
Нам нужно получить данные пользователя, их посты и комментарии к каждому посту с разных конечных точек. Мы будем использовать fetch для сбора данных с нескольких конечных точек и возвращать их через GraphQL.
Обновление резолверов
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;
Обновление схемы 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;
Настройка сервера в server.js
остается прежней. Как только мы обновим код React.js, мы получим следующий вывод:
Результат
Вы увидите подробные данные пользователя, такие как: [Ссылка на Github].
Преимущества этого подхода
Интеграция GraphQL в ваше React-приложение предоставляет несколько преимуществ:
Устранение избыточных запросов
Ключевая особенность GraphQL заключается в том, что он получает только то, что вы запрашиваете. Сервер возвращает только запрашиваемые поля и обеспечивает уменьшение объема передаваемых по сети данных, обслуживая только то, что требует запрос, тем самым улучшая производительность.
Упрощение кода фронтенда
GraphQL позволяет получать необходимую информацию в одном запросе, независимо от ее источника. Внутри он может делать 3 вызова API для получения информации. Это помогает упростить ваш фронтенд-код, потому что теперь вам не нужно координировать разные асинхронные запросы и объединять их результаты.
Улучшение опыта разработчика
Сильная типизация и интуиция схемы предлагают лучшие инструменты и проверку ошибок, чем в традиционной реализации API. Более того, существуют интерактивные среды, где разработчики могут создавать и тестировать запросы, включая GraphiQL или Apollo Explorer.
Решение сложностей и вызовов
Этот подход имеет некоторые преимущества, но также вводит некоторые проблемы, которые необходимо решать.
Дополнительный уровень бэкенда
Введение сервера GraphQL создает дополнительный уровень в вашей архитектуре бэкенда, и если он не управляется должным образом, он становится единой точкой отказа.
Решение
Обратите внимание на обработку ошибок и мониторинг. Контейнеризация и инструменты оркестрации, такие как Docker и Kubernetes, могут помочь управлять масштабируемостью и надежностью.
Потенциальные накладные расходы на производительность
Сервер GraphQL может делать несколько вызовов REST API для разрешения одного запроса, что может ввести задержки и накладные расходы в систему.
Решение
Кэшируйте результаты, чтобы избежать нескольких вызовов к API. Некоторые инструменты, такие как DataLoader, могут обрабатывать процесс пакетирования и кэширования запросов.
Заключение
“Простота — это высшая степень утонченности” — Леонардо да Винчи
Интеграция GraphQL в ваше React-приложение — это не просто оптимизация производительности, это стратегический шаг к созданию более удобных для поддержки, масштабируемых и эффективных приложений. Устраняя избыточные запросы и упрощая управление данными, вы не только улучшаете пользовательский опыт, но и наделяете вашу команду разработчиков лучшими инструментами и практиками.
Хотя внедрение слоя GraphQL сопряжено с определенными трудностями, преимущества часто перевешивают сложности. Тщательно планируя вашу реализацию, оптимизируя ваши резолверы и защищая ваши конечные точки, вы можете смягчить потенциальные недостатки. Более того, гибкость, которую предлагает GraphQL, может сделать ваше приложение устойчивым к изменениям по мере его роста и эволюции.
Принятие GraphQL не означает отказ от ваших существующих REST API. Напротив, это позволяет вам использовать их сильные стороны, предоставляя более эффективный и гибкий уровень доступа к данным для ваших фронтенд-приложений. Этот гибридный подход сочетает в себе надежность REST с гибкостью GraphQL, предлагая вам лучшее из обоих миров.
Если вы готовы поднять ваше React-приложение на новый уровень, подумайте о том, чтобы интегрировать GraphQL в вашу стратегию получения данных. Путь может быть непростым, но награды — более плавный процесс разработки, более счастливые разработчики и довольные пользователи — делают это стоящим начинанием.
Полный код доступен
Вы можете найти полный код этой реализации в моем репозитории на GitHub.
Source:
https://dzone.com/articles/enhancing-react-applications-with-graphql-over-rest-apis