GraphQL so với REST: Cuộc Chiến API
Việc lựa chọn giữa GraphQL và REST cho API của bạn giống như việc lựa chọn giữa một bữa tiệc thức ăn không giới hạn và một thực đơn có mức giá cố định. Cả hai đều có ưu và nhược điểm, và lựa chọn tốt nhất phụ thuộc vào nhu cầu của dự án của bạn. Bài viết này sẽ so sánh hai phương pháp API này từ góc độ của nhà phát triển phía trước, tập trung vào việc lấy dữ liệu (data fetching), quá tải dữ liệu/ít tải dữ liệu (over-fetching/under-fetching), kỹ thuật kiểu mạnh (strong typing), trải nghiệm phát triển (developer experience), bộ nhớ đệm (caching), và các trường hợp sử dụng.
Lấy Dữ Liệu: Hiệu Suất và Linh Hoạt
REST
Trong REST, bạn thực hiện nhiều yêu cầu đến các điểm cuối khác nhau để lấy được dữ liệu cần thiết. Điều này giống như việc gọi món ăn riêng biệt từ thực đơn. Bạn có thể nhận được quá nhiều dữ liệu mà không cần thiết hoặc bạn có thể phải lên nhiều chuyến đến bữa tiệc (API) để lấy tất cả những gì bạn cần. Ví Dụ: Lấy Thông Tin Người Dùng và Bài Viết Bằng REST Giả sử bạn cần lấy thông tin người dùng và bài viết của họ. Với REST, bạn có thể có:
/users/123
để lấy thông tin người dùng./users/123/posts
để lấy bài viết của người dùng. Điều này yêu cầu hai yêu cầu riêng biệt:
// Lấy thông tin người dùng
fetch("https://api.example.com/users/123")
.then((response) => response.json())
.then((data) => console.log(data));
// Lấy bài viết của người dùng
fetch("https://api.example.com/users/123/posts")
.then((response) => response.json())
.then((data) => console.log(data));
GraphQL
Với GraphQL, bạn gửi một truy vấn duy nhất đến một điểm cuối, chỉ định chính xác những dữ liệu bạn cần. Điều này giống như việc bạn tạo ra một bữa ăn trộn riêng của mình tại tiệc cơm ăn không giới hạn. Bạn chỉ nhận được những gì bạn muốn, không nhiều, không ít. Ví Dụ: Lấy Thông Tin Người Dùng và Bài Viết Bằng GraphQL Sử dụng một truy vấn GraphQL đơn, bạn có thể lấy cả thông tin người dùng và bài viết của họ trong một yêu cầu:
# Ví dụ truy vấn GraphQL
query {
user(id: 123) {
name
email
posts {
title
content
}
}
}
Ví Dụ Trong JavaScript:
const query = `
query {
user(id: 123) {
name
email
posts {
title
content
}
}
}
`;
fetch("https://api.example.com/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ query }),
})
.then((response) => response.json())
.then((data) => console.log(data));
Quá Tải Dữ Liệu và Ít Tải Dữ Liệu: Ngăn Chướng Chính Exact
REST
Một trong những điểm yếu chính của REST là khả năng gây ra quá tải dữ liệu và ít tải dữ liệu:
- Over-Fetching (Quá tải dữ liệu): Bạn nhận được nhiều dữ liệu hơn bạn cần, đồng nghĩa với việc lãng phí băng thông và có thể làm chậm ứng dụng của bạn.
- Under-Fetching (Ít tải dữ liệu): Bạn không nhận được tất cả các dữ liệu bạn cần, yêu cầu các yêu cầu bổ sung để lấy thêm dữ liệu. Ví Dụ: Quá Tải Dữ Liệu Trong REST Giả sử bạn chỉ cần tên và email của người dùng. Với REST, bạn có thể nhận được phản hồi với các trường không cần thiết:
{
"id": 123,
"name": "John Doe",
"email": "john.doe@example.com",
"address": {
"street": "123 Main St",
"city": "Anytown",
"state": "CA",
"zip": "12345"
},
"phone": "555-1234"
}
GraphQL
GraphQL giải quyết vấn đề quá tải và ít tải dữ liệu bằng cách cho phép bạn yêu cầu chính xác những dữ liệu bạn cần, không nhiều, không ít. Điều này giống như việc gọi lại một nửa phần của món ăn tại nhà hàng - bạn chỉ nhận được những gì bạn muốn mà không lãng phí thức ăn (hoặc băng thông). Ví Dụ: Chỉ Lấy Các Trường Cần Thiết Trong GraphQL
# Ví dụ truy vấn GraphQL
query {
user(id: 123) {
name
email
}
}
Truy vấn này chỉ lấy ra các trường name
và email
của người dùng.
Kiểu Đáng Tin Cậy và Khám Phá: Độc Đáo Của GraphQL
REST
Trong REST, bạn thường phải dựa vào tài liệu để mô tả cấu trúc và các loại dữ liệu của API. Tài liệu có thể lỗi thời hoặc không chính xác, dẫn đến hiểu lầm và lỗi. Điều này giống như việc lắp ráp đồ gỗ IKEA mà không có hướng dẫn. Ví Dụ: Tài Liệu REST
# Điểm cuối Người dùng
GET /users/{id}
**Phản hồi:**
{
"id": "integer",
"name": "string",
"email": "string",
"address": {
"street": "string",
"city": "string",
"state": "string",
"zip": "string"
},
"phone": "string"
}
GraphQL
GraphQL có một lược đồ kiểu mạnh mẽ định nghĩa các loại dữ liệu có sẵn và mối quan hệ giữa chúng. Lược đồ này cho phép các khả năng khám phá mạnh mẽ, cho phép bạn khám phá API và phát hiện dữ liệu nào có sẵn. Điều này giống như việc sở hữu một cuốn hướng dẫn IKEA đi kèm - rõ ràng, tóm tắt và luôn cập nhật. Ví Dụ: Lược đồ GraphQL
type User {
id: ID!
name: String!
email: String!
address: Address
phone: String
}
type Address {
street: String
city: String
state: String
zip: String
}
type Query {
user(id: ID!): User
}
Khám Phá Trong GraphQL:
# Truy vấn Khám phá
{
__schema {
types {
name
fields {
name
type {
name
}
}
}
}
}
Trải Nghiệm Phát Triển và Công Cụ: Làm Cuộc Đời Đơn Giản Hơn
REST
Công cụ và thư viện cho REST rất phong phú, nhưng có thể có sự không nhất quán và biến thể về cách thức hoạt động. Ví Dụ: Thư Viện REST
- Axios: Một HTTP client phổ biến để thực hiện các yêu cầu.
- Fetch API: BUILTIN vào trình duyệt hiện đại để thực hiện các yêu cầu. Sử dụng Axios Trong REST:
import axios from "axios";
axios
.get("https://api.example.com/users/123")
.then((response) => console.log(response.data))
.catch((error) => console.error(error));
GraphQL
GraphQL có hệ sinh thái công cụ và thư viện phát triển nhanh chóng như GraphiQL và Apollo Client, cung cấp các tính năng như tự hoàn thành, khám phá lược đồ và bộ nhớ đệm. Những công cụ này cải thiện trải nghiệm của nhà phát triển đáng kể. Ví Dụ: Apollo Client Trong GraphQL
# Cài đặt Apollo Client
npm install @apollo/client graphql
Cài Đặt Apollo Client:
import { ApolloClient, InMemoryCache, gql } from "@apollo/client";
const client = new ApolloClient({
uri: "https://api.example.com/graphql",
cache: new InMemoryCache(),
});
// Sử dụng client để thực hiện một truy vấn
client
.query({
query: gql`
query {
user(id: 123) {
name
email
posts {
title
content
}
}
}
`,
})
.then((response) => console.log(response.data))
.catch((error) => console.error(error));
Bộ nhớ đệm: Tiết Kiệm Băng Thông và Thời Gian
REST
Bộ nhớ đệm trong REST khá trực tiếp do sử dụng nhiều điểm cuối. Mỗi điểm cuối có thể có chiến lược bộ nhớ đệm riêng. Ví Dụ: Bộ nhớ đệm REST với Tiêu Đề
fetch("https://api.example.com/users/123", {
headers: {
"Cache-Control": "max-age=3600", // Bộ nhớ đệm trong 1 giờ
},
});
GraphQL
Bộ nhớ đệm trong GraphQL có thể trở nên khó khăn hơn vì tất cả các yêu cầu đi đến một điểm cuối duy nhất. Tuy nhiên, các công cụ như Apollo Client cung cấp các cơ chế bộ nhớ đệm phức tạp để tối ưu hóa hiệu suất. Ví Dụ: Bộ nhớ đệm Apollo Client
const client = new ApolloClient({
uri: "https://api.example.com/graphql",
cache: new InMemoryCache({
typePolicies: {
User: {
keyFields: ["id"],
},
},
}),
});
// Lấy dữ liệu với bộ nhớ đệm
client
.query({
query: gql`
query {
user(id: 123) {
name
email
posts {
title
content
}
}
}
`,
})
.then((response) => console.log(response.data))
.catch((error) => console.error(error));
Các Trường Hợp Sử Dụng: Chọn Đúng Công Cụ Cho Công Việc
REST
REST phù hợp với:
- API đơn giản: Khi mô hình dữ liệu của bạn đơn giản và không thay đổi thường xuyên.
- Ứng dụng dựa trên nguồn lực: Khi API của bạn được tổ chức xung quanh các nguồn lực (ví dụ: người dùng, bài viết).
- Các tình huống mà bộ nhớ đệm là quan trọng: Khi việc giảm lượng truyền dữ liệu và cải thiện hiệu suất thông qua bộ nhớ đệm là đáng kể. Ví Dụ: REST Trong Một Nền Tảng Đóng Bài Một nền tảng đóng bài đơn giản với API REST có thể định nghĩa các điểm kết thúc như:
/users
để quản lý dữ liệu người dùng./posts
để quản lý các bài viết đăng.
GraphQL
GraphQL phù hợp với:
- Ứng dụng phức tạp: Khi ứng dụng của bạn có yêu cầu về dữ liệu thực sự và các cấu trúc dữ liệu tương quan.
- Ứng dụng di động: Nơi việc giảm lượng truyền dữ liệu và tối ưu hóa hiệu suất rất quan trọng.
- Các tình huống mà linh hoạt là chìa khóa: Khi bạn cần khả năng yêu cầu chỉ những dữ liệu bạn cần. Ví Dụ: GraphQL Trong Ứng Dụng Di Động Một ứng dụng di động yêu cầu cập nhật thường xuyên và truy cập vào nhiều dữ liệu liên quan có thể sử dụng GraphQL để lấy dữ liệu một cách hiệu quả:
# Truy vấn GraphQL cho ứng dụng di động
query {
user(id: 123) {
name
email
profilePicture
posts(limit: 10) {
title
content
comments(limit: 3) {
user {
name
}
text
}
}
}
}
Ví Dụ Thực Tế: Công Ty Thật XYZ
Hãy xem xét Công Ty Thật XYZ, một công ty công nghệ phát triển một nền tảng mạng xã hội. Họ ban đầu đã sử dụng REST nhưng gặp khó khăn với quá tải và ít tải dữ liệu, dẫn đến thời gian tải chậm hơn và tỷ lệ chi phí băng thông cao hơn. Họ quyết định chuyển sang GraphQL để cải thiện hiệu suất và hiệu quả làm việc của nhà phát triển. Dưới đây là cách họ thực hiện điều đó:
Trước: Sử dụng REST
Điểm Kết Thúc:
/users/{id}
/users/{id}/posts
/posts/{id}/comments
Các Yêu Cầu Đa Chục:
// Lấy dữ liệu người dùng
fetch("https://api.realcompanyxyz.com/users/123")
.then((response) => response.json())
.then((userData) => {
console.log("Dữ liệu Người Dùng:", userData);
// Lấy bài viết của người dùng
fetch(`https://api.realcompanyxyz.com/users/${userData.id}/posts`)
.then((response) => response.json())
.then((postsData) => {
console.log("Dữ liệu Bài Viết:", postsData);
postsData.forEach((post) => {
// Lấy bình luận cho mỗi bài viết
fetch(`https://api.realcompanyxyz.com/posts/${post.id}/comments`)
.then((response) => response.json())
.then((commentsData) => {
console.log("Dữ liệu Bình Luận:", commentsData);
});
});
});
});
Sau: Sử dụng GraphQL
Truy Vấn Đơn Một:
# Ví dụ truy vấn GraphQL
query {
user(id: 123) {
name
email
profilePicture
posts(limit: 10) {
title
content
comments(limit: 3) {
user {
name
}
text
}
}
}
}
Mã JavaScript:
import { ApolloClient, InMemoryCache, gql } from "@apollo/client";
const client = new ApolloClient({
uri: "https://api.realcompanyxyz.com/graphql",
cache: new InMemoryCache({
typePolicies: {
User: {
keyFields: ["id"],
},
Post: {
keyFields: ["id"],
},
},
}),
});
client
.query({
query: gql`
query {
user(id: 123) {
name
email
profilePicture
posts(limit: 10) {
title
content
comments(limit: 3) {
user {
name
}
text
}
}
}
}
`,
})
.then((response) => console.log(response.data))
.catch((error) => console.error(error));
Bằng cách chuyển sang GraphQL, Công Ty Thật XYZ đã giảm số lượng yêu cầu từ nhiều xuống còn một, cải thiện thời gian tải và giảm bớt chi phí băng thông. Nhà phát triển cũng đã tận dụng được các tính năng lược đồ mạnh mẽ và khám phá, khiến quá trình phát triển hiệu quả và thú vị hơn bao giờ hết.
Kết Luận:
Không có người chiến thắng rõ ràng trong cuộc chiến GraphQL so với REST. Lựa chọn tốt nhất phụ thuộc vào nhu cầu và ưu tiên cụ thể của dự án của bạn.
- REST: Ideals cho các API đơn giản, các ứng dụng dựa trên nguồn lực, và các tình huống mà bộ nhớ đệm là quan trọng.
- GraphQL: Hoàn hảo cho các ứng dụng phức tạp với các yêu cầu dữ liệu thực sự và các tình huống mà việc giảm lượng truyền dữ liệu là ưu tiên hàng đầu.