在網頁開發中,優化和擴展應用程序始終是一個問題。React.js 作為前端開發的工具取得了非凡的成功,提供了一種強大的方式來創建用戶界面。但隨著應用程序的增長,事情變得複雜,尤其是當涉及到多個 REST API 端點時。過度獲取等問題,即需要過多數據的情況,可能會成為性能瓶頸和不良用戶體驗的根源。
解決這些挑戰的方案之一是將 GraphQL 與 React 應用程序集成。如果你的後端有多個 REST 端點,那麼引入一個內部調用你的 REST API 端點的 GraphQL 層可以防止過度獲取並簡化你的前端應用程序。在本文中,你將找到如何使用它、這種方法的優缺點、各種挑戰以及如何應對這些挑戰。我們還將深入探討一些實際示例,展示 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
建立一個名為 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;
這個 schema 定義了一個 User
類型和一個透過 ID 擷取使用者的 user
查詢。
實作 Resolvers
建立一個名為 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 連結]。
與多個端點合作
想像一個場景,您需要檢索特定用戶的帖子,以及每個帖子的個別評論。與其從您的前端 React 應用程序發出三個單獨的 API 調用並處理不必要的數據,不如使用 GraphQL 精簡流程。通過定義模式並編寫 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