Xin chào mừng các bạn quay trở lại với chuỗi bài viết React Roadmap! Chúng ta đã cùng nhau đi qua những khái niệm cốt lõi nhất của React như React là gì, sự khác biệt giữa Class và Functional Components, cách hoạt động của JSX, quản lý dữ liệu với Props và State, hay sử dụng sức mạnh của useState và useEffect cùng Custom Hooks. Chúng ta đã biết cách kết hợp các component, làm việc với danh sách, xử lý sự kiện, và thậm chí là quản lý state phức tạp hơn với useReducer hay useContext (và cả các lựa chọn state management khác như Redux Toolkit, Recoil, Zustand hay MobX).
Tuy nhiên, một ứng dụng frontend thực tế hiếm khi chỉ chạy trên trình duyệt mà không cần dữ liệu từ bên ngoài. Dữ liệu này thường đến từ một backend server thông qua các API. Với tư cách là một React developer, việc hiểu và lựa chọn cách tương tác với API là một kỹ năng vô cùng quan trọng.
Trong thế giới hiện đại, có hai phương pháp phổ biến nhất để xây dựng API cho ứng dụng web và mobile: REST và GraphQL. Cả hai đều có những ưu điểm và nhược điểm riêng, và việc lựa chọn giữa chúng có thể ảnh hưởng đáng kể đến hiệu suất, trải nghiệm phát triển và cấu trúc của ứng dụng React của bạn. Bài viết này sẽ đi sâu vào so sánh hai phương pháp này, đặc biệt là từ góc nhìn của một React developer, giúp bạn đưa ra quyết định sáng suốt cho dự án của mình.
Mục lục
REST: Nền Tảng Vững Chắc
REST (Representational State Transfer) không phải là một tiêu chuẩn hay framework, mà là một kiến trúc cho các hệ thống phân tán. Nó dựa trên các nguyên tắc của giao thức HTTP và sử dụng các phương thức HTTP tiêu chuẩn (GET, POST, PUT, DELETE, PATCH) để thao tác với các tài nguyên (resources) thông qua các URL duy nhất. REST đã thống trị thế giới API trong nhiều năm và vẫn là lựa chọn mặc định cho nhiều dự án.
Đối với một React developer, việc làm việc với API REST thường xoay quanh việc gửi các yêu cầu HTTP đến các endpoint khác nhau và xử lý dữ liệu nhận được. Chúng ta đã tìm hiểu cách thực hiện điều này bằng cách sử dụng Fetch API hoặc thư viện Axios trong một bài viết trước.
Làm Việc Với REST trong React
Quá trình cơ bản khi sử dụng REST trong React thường bao gồm:
- Xác định endpoint cần gọi (ví dụ: `/api/users`, `/api/products/123`).
- Sử dụng một thư viện HTTP client (như Fetch hoặc Axios) để gửi yêu cầu với phương thức HTTP phù hợp (GET để lấy dữ liệu, POST để tạo, PUT/PATCH để cập nhật, DELETE để xóa).
- Sử dụng các React Hook như
useEffect
để gọi API khi component được mount hoặc khi dependency thay đổi. - Quản lý trạng thái (loading, error, data) của yêu cầu API trong state của component (ví dụ: sử dụng
useState
). - Render giao diện dựa trên dữ liệu nhận được hoặc hiển thị thông báo loading/error.
Một ví dụ đơn giản với Axios và Hook:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
axios.get('/api/users')
.then(response => {
setUsers(response.data);
setLoading(false);
})
.catch(error => {
setError(error);
setLoading(false);
});
}, []); // Empty dependency array means this runs once on mount
if (loading) return <p>Đang tải...</p>;
if (error) return <p>Lỗi khi tải dữ liệu: {error.message}</p>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name} ({user.email})</li>
))}
</ul>
);
}
export default UserList;
Đoạn code trên minh họa cách sử dụng useEffect
để fetch dữ liệu danh sách người dùng từ endpoint /api/users
khi component UserList
được hiển thị. Đây là cách tiếp cận rất phổ biến khi làm việc với REST API trong React.
Ưu Điểm Của REST Đối Với React Developer
- Sự Trưởng Thành và Phổ Biến: REST đã tồn tại rất lâu và là mô hình API phổ biến nhất. Điều này có nghĩa là có rất nhiều tài liệu, cộng đồng hỗ trợ lớn, và vô số công cụ, thư viện (như Axios, Fetch API tích hợp sẵn, các thư viện quản lý cache như React Query hoặc SWR) sẵn sàng để bạn sử dụng trong ứng dụng React của mình.
- Đơn Giản Với Các Use Case Cơ Bản: Đối với các API đơn giản, chỉ cần lấy hoặc gửi dữ liệu cho một tài nguyên cụ thể (ví dụ: lấy thông tin chi tiết một sản phẩm), REST rất trực quan và dễ thực hiện. Mỗi endpoint rõ ràng, dễ hiểu.
- Cơ Chế Cache Tích Hợp Của HTTP: REST tận dụng tốt các cơ chế caching tiêu chuẩn của HTTP (như ETag, Last-Modified). Trình duyệt và các proxy có thể tự động cache response, giúp giảm tải cho server và tăng tốc độ tải dữ liệu cho người dùng. Điều này có lợi cho hiệu suất ứng dụng React mà không cần cấu hình phức tạp ở tầng ứng dụng.
- Tương Thích Rộng Rãi: Hầu hết các dịch vụ web công cộng (public APIs) đều cung cấp API theo kiến trúc REST. Nếu ứng dụng React của bạn cần tích hợp với nhiều dịch vụ bên ngoài, REST là lựa chọn khả thi và dễ dàng hơn.
Nhược Điểm Của REST Đối Với React Developer
- Over-fetching và Under-fetching: Đây là vấn đề lớn nhất mà React developer thường gặp phải với REST. Khi bạn gọi một endpoint (ví dụ: `/api/users`), server sẽ trả về toàn bộ dữ liệu về người dùng (id, tên, email, địa chỉ, ngày sinh, v.v.). Nếu bạn chỉ cần hiển thị tên và email trong component, bạn đang over-fetching (lấy thừa dữ liệu không cần thiết). Ngược lại, nếu bạn cần hiển thị tên người dùng và danh sách các bài viết của họ, bạn có thể cần gọi hai endpoint riêng biệt (ví dụ: `/api/users/1` và `/api/posts?userId=1`). Đây là under-fetching (lấy thiếu dữ liệu cần thiết trong một lần gọi) hoặc dẫn đến vấn đề N+1 request (gọi API N lần cho N bài viết sau khi đã lấy danh sách).
- Nhiều Roundtrip: Như ví dụ N+1 ở trên, để hiển thị một màn hình với dữ liệu từ nhiều tài nguyên liên quan, ứng dụng React của bạn có thể phải thực hiện nhiều yêu cầu API liên tiếp hoặc song song. Điều này làm tăng độ trễ (latency), phức tạp hóa việc quản lý trạng thái loading và error trên frontend.
- Khó Khăn Khi Nhu Cầu Dữ Liệu Thay Đổi: Khi yêu cầu của frontend thay đổi (ví dụ: cần thêm một trường dữ liệu mới cho người dùng), backend developer cần sửa đổi hoặc tạo endpoint mới. Điều này tạo ra sự phụ thuộc chặt chẽ giữa frontend và backend, làm chậm quá trình phát triển.
- Versioning API: Khi API thay đổi, bạn thường phải quản lý các phiên bản khác nhau (ví dụ: `/api/v1/users`, `/api/v2/users`) để tránh làm hỏng các client cũ. Điều này thêm gánh nặng cho cả backend và frontend.
GraphQL: Linh Hoạt và Mạnh Mẽ Hơn?
GraphQL là một ngôn ngữ truy vấn cho API của bạn (A query language for your API) và cũng là một runtime để thực hiện các truy vấn đó bằng cách sử dụng dữ liệu hiện có của bạn. Được phát triển bởi Facebook (nay là Meta) vào năm 2012 và public vào năm 2015, GraphQL được thiết kế để giải quyết những hạn chế mà họ gặp phải với REST, đặc biệt trong các ứng dụng mobile phức tạp với nhu cầu dữ liệu đa dạng.
Điểm khác biệt cốt lõi là với GraphQL, client (ứng dụng React của bạn) có thể chủ động yêu cầu chính xác những dữ liệu cần thiết. Thay vì nhiều endpoint cho các tài nguyên khác nhau, GraphQL thường chỉ có một endpoint duy nhất (thường là /graphql
) nơi client gửi các truy vấn hoặc mutation.
Làm Việc Với GraphQL trong React
Để sử dụng GraphQL hiệu quả trong React, bạn thường sẽ sử dụng các thư viện client chuyên dụng như Apollo Client, Relay, hoặc Urql. Các thư viện này cung cấp các công cụ mạnh mẽ để:
- Gửi các truy vấn (Queries) để lấy dữ liệu.
- Gửi các mutation (Mutations) để thay đổi dữ liệu (tạo, cập nhật, xóa).
- Thiết lập các subscriptions (Subscriptions) để nhận dữ liệu theo thời gian thực.
- Quản lý cache dữ liệu một cách tự động và thông minh.
- Tích hợp sâu với React thông qua các Hook (như
useQuery
,useMutation
,useSubscription
).
Ví dụ về một truy vấn GraphQL và cách sử dụng với Apollo Client trong React:
import React from 'react';
import { useQuery, gql } from '@apollo/client';
// Định nghĩa truy vấn GraphQL
const GET_USERS = gql`
query GetUsers {
users {
id
name
email
}
}
`;
function UserListGraphQL() {
// Sử dụng hook useQuery để thực hiện truy vấn
const { loading, error, data } = useQuery(GET_USERS);
if (loading) return <p>Đang tải...</p>;
if (error) return <p>Lỗi khi tải dữ liệu: {error.message}</p>;
return (
<ul>
{data.users.map(user => (
<li key={user.id}>{user.name} ({user.email})</li>
))}
</ul>
);
}
// Component gốc của ứng dụng cần được bọc bởi ApolloProvider
// import { ApolloProvider, ApolloClient, InMemoryCache } from '@apollo/client';
// const client = new ApolloClient({
// uri: '/graphql', // Endpoint GraphQL
// cache: new InMemoryCache()
// });
// function App() {
// return (
// <ApolloProvider client={client}>
// <UserListGraphQL />
// </ApolloProvider>
// );
// }
export default UserListGraphQL;
Ở đây, truy vấn GET_USERS
yêu cầu chỉ lấy id
, name
, và email
của người dùng. Hook useQuery
từ Apollo Client giúp xử lý việc fetch dữ liệu, quản lý trạng thái loading/error và trả về dữ liệu (trong đối tượng data
) sẵn sàng để render. Nếu sau này bạn cần thêm trường phone
, bạn chỉ việc sửa truy vấn trong component:
const GET_USERS = gql`
query GetUsers {
users {
id
name
email
phone # Thêm trường phone ở đây
}
}
`;
Backend không cần thay đổi endpoint, chỉ cần đảm bảo schema GraphQL có trường phone
và resolver xử lý được nó. Đây chính là sức mạnh của việc client yêu cầu dữ liệu!
Ưu Điểm Của GraphQL Đối Với React Developer
- Loại Bỏ Over-fetching và Under-fetching: Đây là lợi ích lớn nhất. React component của bạn chỉ yêu cầu chính xác dữ liệu nó cần, không hơn không kém. Điều này giúp giảm thiểu lượng dữ liệu truyền qua mạng, đặc biệt quan trọng trên các thiết bị di động hoặc mạng chậm. Frontend có toàn quyền kiểm soát cấu trúc của response.
- Giảm Số Lượng Roundtrip: Với một truy vấn duy nhất, bạn có thể lấy dữ liệu từ nhiều tài nguyên liên quan (ví dụ: thông tin người dùng và danh sách bài viết của họ) trong một lần gọi API. Điều này giảm đáng kể độ trễ và đơn giản hóa logic quản lý dữ liệu ở frontend.
- Phát Triển Nhanh Chóng và Linh Hoạt Hơn: Frontend developer có thể tự tin thay đổi yêu cầu dữ liệu mà không cần chờ backend thay đổi endpoint. Miễn là dữ liệu đó có sẵn trong schema GraphQL, frontend có thể truy vấn nó. Điều này thúc đẩy sự song song trong quá trình phát triển giữa frontend và backend.
- Schema Mạnh Mẽ và Introspection: GraphQL có một hệ thống kiểu dữ liệu (schema) rất rõ ràng. Điều này cho phép các công cụ phát triển mạnh mẽ như GraphiQL (một IDE cho GraphQL) giúp bạn khám phá API, kiểm tra truy vấn, và nhận phản hồi về kiểu dữ liệu. Client libraries cũng có thể tận dụng schema để validate truy vấn ở compile-time hoặc runtime, giúp bắt lỗi sớm.
- Subscriptions Cho Dữ Liệu Thời Gian Thực: GraphQL có cơ chế tích hợp sẵn cho dữ liệu thời gian thực (subscriptions), rất hữu ích cho các tính năng như chat, thông báo, hoặc cập nhật trực tiếp trên giao diện mà không cần dùng polling hay WebSockets thủ công.
- Trải Nghiệm Phát Triển Tốt Hơn Với Client Libraries: Các thư viện như Apollo Client cung cấp nhiều tính năng hữu ích cho React developer như quản lý cache thông minh (normalize cache), tích hợp với state management (như Apollo Link State, hoặc kết hợp với Redux/Context/Recoil), quản lý loading/error state tự động, optimistic updates, pagination, v.v.
Nhược Điểm Của GraphQL Đối Với React Developer
- Độ Phức Tạp Cao Hơn: GraphQL có đường cong học tập dốc hơn so với REST, đặc biệt là với các junior developer. Bạn cần hiểu về schema, resolvers (ở phía backend), query, mutation, subscription, và cách sử dụng client library hiệu quả.
- Yêu Cầu Hỗ Trợ Từ Backend: Để sử dụng GraphQL, backend của bạn cần xây dựng một GraphQL schema và triển khai các resolvers. Nếu bạn chỉ là một frontend developer làm việc với một backend REST sẵn có, việc chuyển sang GraphQL có thể là không khả thi trừ khi backend team đồng ý xây dựng API GraphQL.
- Quản Lý Cache Phức Tạp Hơn: Caching trong GraphQL khác với caching HTTP truyền thống. Client libraries như Apollo Client cung cấp cache memory, nhưng việc cấu hình và hiểu cách nó hoạt động có thể phức tạp hơn so với chỉ dựa vào cache của trình duyệt với REST.
- File Uploading: Việc upload file không phải là một phần của đặc tả GraphQL ban đầu, mặc dù hiện tại có các cách chuẩn để xử lý nó thông qua các extension. Nó vẫn có thể yêu cầu cấu hình bổ sung so với việc gửi file trong request POST/PUT của REST.
- Performance Monitoring và Error Handling: Với chỉ một endpoint, việc theo dõi hiệu suất của từng “truy vấn con” hoặc hiểu rõ lỗi xảy ra ở đâu trong một truy vấn phức tạp có thể đòi hỏi các công cụ và phương pháp tiếp cận khác biệt so với việc giám sát các endpoint REST riêng lẻ.
- Ecosystem Mới Hơn: Mặc dù GraphQL đang phát triển rất nhanh, hệ sinh thái công cụ và tài nguyên (đặc biệt là các dịch vụ public APIs) vẫn chưa đồ sộ bằng REST.
REST vs GraphQL Trong React: Bảng So Sánh
Để dễ hình dung hơn, đây là bảng so sánh tóm tắt các điểm chính từ góc độ của một React developer:
Đặc Điểm | REST | GraphQL |
---|---|---|
Khái Niệm | Kiến trúc dựa trên tài nguyên (resources) và HTTP methods. | Ngôn ngữ truy vấn dữ liệu cho API. |
Fetching Data | Nhiều endpoints cho các tài nguyên khác nhau. | Thường là một endpoint duy nhất. Client yêu cầu dữ liệu cần thiết. |
Over/Under-fetching | Thường xảy ra. Client nhận toàn bộ dữ liệu của resource hoặc phải gọi nhiều endpoint. | Không xảy ra. Client chỉ nhận chính xác dữ liệu đã yêu cầu. |
Số Lượng Roundtrip | Có thể cần nhiều roundtrip để lấy dữ liệu liên quan từ các resource khác nhau. | Thường chỉ cần một roundtrip duy nhất cho dữ liệu phức tạp. |
Caching | Tận dụng cache HTTP của trình duyệt/proxy. | Quản lý cache phức tạp hơn, thường dựa vào cache của client library. |
Versioning | Thường quản lý qua URL (v1, v2). | Ít cần versioning API ở cấp độ cao; có thể thêm/bớt trường trong schema. |
Độ Phức Tạp (Frontend) | Dễ bắt đầu với Fetch/Axios. | Cần học về query/mutation/subscription và cách dùng client library. |
Độ Phức Tạp (Backend) | Tương đối đơn giản để xây dựng các endpoint cơ bản. | Cần xây dựng schema và resolvers, phức tạp hơn ở giai đoạn đầu. |
Công Cụ/Ecosystem | Rất trưởng thành, nhiều thư viện, công cụ. | Đang phát triển nhanh, có các client library mạnh mẽ và công cụ introspection. |
Real-time Data | Cần WebSockets, polling hoặc các công nghệ khác bên ngoài kiến trúc cốt lõi. | Có cơ chế Subscriptions tích hợp. |
Khi Nào Nên Chọn REST, Khi Nào Nên Chọn GraphQL Cho Ứng Dụng React?
Quyết định lựa chọn giữa REST và GraphQL phụ thuộc vào nhiều yếu tố của dự án và đội ngũ của bạn:
- Chọn REST khi:
- Ứng dụng của bạn đơn giản, nhu cầu dữ liệu không quá phức tạp và ít thay đổi.
- Backend đã có sẵn API REST và việc xây dựng lại là không khả thi hoặc không cần thiết.
- Bạn cần tích hợp với nhiều dịch vụ bên thứ ba chỉ cung cấp API REST công khai.
- Đội ngũ backend hoặc frontend chưa quen thuộc với GraphQL và thời gian/nguồn lực để học là hạn chế.
- Các cơ chế cache HTTP sẵn có đủ đáp ứng yêu cầu hiệu năng.
- Chọn GraphQL khi:
- Ứng dụng của bạn có nhu cầu dữ liệu phức tạp, các tài nguyên có mối quan hệ lồng ghép sâu sắc (graph-like data).
- Ứng dụng là mobile-first, nơi việc giảm thiểu dữ liệu và roundtrip là rất quan trọng.
- Bạn dự đoán nhu cầu dữ liệu của frontend sẽ thay đổi thường xuyên và muốn giảm sự phụ thuộc vào backend.
- Đội ngũ phát triển (cả frontend và backend) sẵn sàng đầu tư thời gian để học và áp dụng GraphQL.
- Bạn cần các tính năng real-time (subscriptions) hoặc muốn tận dụng các tính năng quản lý cache/state mạnh mẽ của client library GraphQL.
- Frontend developer muốn có nhiều quyền kiểm soát hơn về dữ liệu nhận được.
Một điều quan trọng cần lưu ý là bạn không nhất thiết phải chọn hoàn toàn REST hoặc hoàn toàn GraphQL cho toàn bộ ứng dụng. Trong một số trường hợp, kiến trúc lai (hybrid) cũng là một lựa chọn, sử dụng REST cho một số phần và GraphQL cho những phần khác cần sự linh hoạt cao hơn.
Tích Hợp Vào Dự Án React
Việc lựa chọn kiến trúc API sẽ ảnh hưởng đến các thư viện và cách bạn quản lý dữ liệu trong ứng dụng React. Với REST, bạn có thể chỉ cần Fetch/Axios hoặc kết hợp với các thư viện quản lý cache/data fetching chuyên dụng như React Query hoặc SWR. Các thư viện này giúp bạn xử lý các vấn đề như caching, revalidation, background fetching, error handling một cách hiệu quả hơn so với việc tự viết logic bằng useEffect
và useState
đơn thuần. Chúng cũng có thể tích hợp tốt với các thư viện quản lý state khác như Redux nếu cần.
Với GraphQL, bạn gần như chắc chắn sẽ sử dụng một client library chuyên dụng như Apollo Client hoặc Urql. Các thư viện này không chỉ giúp gửi query/mutation mà còn đóng vai trò là một lớp quản lý cache và thậm chí là một giải pháp quản lý state cục bộ (local state management) cho dữ liệu API. Việc tích hợp với các thư viện state management khác như Redux vẫn khả thi nhưng có thể cần cấu hình thêm.
Lời Kết
REST và GraphQL đều là những cách tiếp cận mạnh mẽ để xây dựng API và tương tác với dữ liệu từ ứng dụng React. REST là lựa chọn an toàn, trưởng thành và dễ tiếp cận cho các dự án không quá phức tạp. GraphQL mang lại sự linh hoạt, hiệu quả về dữ liệu và trải nghiệm phát triển tốt hơn cho các ứng dụng có nhu cầu dữ liệu đa dạng và phức tạp, nhưng đòi hỏi đầu tư ban đầu lớn hơn và sự phối hợp chặt chẽ giữa frontend và backend.
Với tư cách là một React developer đang trên Lộ trình học React 2025, điều quan trọng không phải là tuyên bố phương pháp nào “tốt hơn” mà là hiểu rõ ưu nhược điểm của từng loại để có thể đưa ra quyết định phù hợp với ngữ cảnh dự án cụ thể của bạn. Hãy thử nghiệm cả hai, hiểu sâu cách chúng hoạt động, và bạn sẽ trang bị cho mình những kỹ năng cần thiết để xây dựng các ứng dụng React mạnh mẽ và hiệu quả.
Bài viết tiếp theo trong chuỗi React Roadmap, chúng ta sẽ chuyển hướng sang một chủ đề cũng rất quan trọng: testing trong React. Làm thế nào để đảm bảo các component và logic của bạn hoạt động đúng như mong đợi? Đừng bỏ lỡ nhé!