在网页开发中,优化和扩展应用程序一直是一个问题。React.js作为一种工具在前端开发中取得了非凡的成功,提供了一种强大的方式来创建用户界面。但随着应用程序的增长,尤其是涉及多个REST API端点时,这变得复杂。诸如过度获取(over-fetching)等问题,即需要过多的数据,可能会导致性能瓶颈和糟糕的用户体验。
应对这些挑战的解决方案之一是将GraphQL与React应用程序结合使用。如果你的后端有多个REST端点,那么引入一个GraphQL层来内部调用你的REST API端点可以减少过度获取并简化你的前端应用程序。在本文中,你将找到如何使用它、这种方法的优缺点、各种挑战以及如何应对这些挑战。我们还将深入一些实际示例,展示GraphQL如何帮助你改善数据处理方式。
REST API中的过度获取
在REST API中,过度获取发生在API向客户端提供的数据量超过客户端所需的数据量。这是REST API中一个常见的问题,通常返回固定的对象或响应模式。为了更好地理解这个问题,让我们考虑一个例子。
考虑一个用户资料页面,只需要显示用户的name
和email
。使用典型的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调用。
这种方法使您能够优化数据获取,而无需全面改造现有的后端基础设施。
在React应用程序中实现GraphQL
让我们看一下如何设置一个 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
类型和一个通过 ID 获取用户的 user
查询。
实现解析器
创建一个名为 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 应用程序以使用 GraphQL API。
安装 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链接]。
使用多个端点
想象一种情景,您需要检索特定用户的帖子,以及每个帖子的个别评论。您可以通过GraphQL简化流程,而不是从前端React应用程序进行三次单独的API调用并处理不必要的数据。通过定义架构和构建GraphQL查询,您可以仅请求UI所需的确切数据,这是一次高效的请求。
我们需要从不同端点获取用户数据、他们的帖子以及每个帖子的评论。我们将使用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