Share

Giải pháp API: GraphQL so găng cùng RESTful API

Giải pháp API: GraphQL so găng cùng RESTful API

Hi anh em. Anh em chắc chắn đã quá quen thuộc với việc xây dựng và sử dụng API để kết nối các ứng dụng web và di động. Ngày nay, ứng dụng của chúng ta ngày càng phức tạp, đòi hỏi nhiều dữ liệu hơn, từ nhiều nguồn khác nhau. Điều này dẫn đến nhu cầu tìm kiếm những phương pháp hiệu quả hơn để lấy dữ liệu từ server.

Trong bối cảnh đó, GraphQL nổi lên như một “ngôi sao mới”, một giải pháp đầy tiềm năng để thay thế hoặc bổ sung cho REST API quen thuộc. Vậy GraphQL là gì mà lại được nhiều người nhắc đến vậy?

Bài viết này, SiuCode sẽ giúp anh em:

  • Hiểu rõ GraphQL là gì một cách chi tiết và dễ hiểu.
  • So sánh GraphQL với REST API trên nhiều khía cạnh quan trọng.
  • Cung cấp thông tin để anh em có thể quyết định khi nào nên và không nên sử dụng GraphQL cho dự án của mình.

Nào, lên thuyền và ra khơi thôi.

GraphQL là gì?

Định nghĩa chi tiết về GraphQL:

GraphQL không chỉ đơn thuần là một cách để lấy dữ liệu từ server mà còn là một ngôn ngữ truy vấn và thao tác dữ liệu cho API. Điểm mạnh của GraphQL nằm ở khả năng cho phép client (ứng dụng của chúng ta) được tự do chỉ định chính xác dữ liệu mà họ cần, không hơn không kém.

Các khái niệm cốt lõi của GraphQL:

Để hiểu rõ hơn về GraphQL, chúng ta cần nắm vững một số khái niệm quan trọng sau:

  • Schema: Anh em cứ hình dung schema giống như bản thiết kế của API vậy. Nó định nghĩa cấu trúc dữ liệu mà API cung cấp, bao gồm các loại dữ liệu (types) và các thao tác (operations) mà client có thể thực hiện. Schema được viết bằng một ngôn ngữ định nghĩa schema (Schema Definition Language – SDL) rất dễ đọc.
  • Query: Đây là cách mà client sử dụng để yêu cầu dữ liệu cụ thể từ server. Một query trong GraphQL sẽ mô tả chính xác những trường (fields) nào mà client muốn nhận về cho một loại dữ liệu nào đó.
  • Mutation: Nếu query dùng để lấy dữ liệu thì mutation dùng để thực hiện các thao tác thay đổi dữ liệu trên server. Các thao tác này có thể là tạo mới (create), cập nhật (update) hoặc xóa (delete) dữ liệu.
  • Subscription: Đây là một tính năng rất hay của GraphQL, cho phép client thiết lập một kết nối real-time với server. Khi dữ liệu trên server có sự thay đổi liên quan đến subscription này, server sẽ tự động push thông báo về cho client.

Ví dụ minh họa đơn giản:

Để anh em dễ hình dung, hãy so sánh một tình huống đơn giản khi muốn lấy thông tin của một người dùng (user) với ID là “123” bằng cả REST APIGraphQL.

Với REST API:

Thông thường, chúng ta sẽ gửi một request GET đến một endpoint cụ thể, ví dụ như /users/123. Server sẽ trả về một response có thể chứa nhiều thông tin về user này, ví dụ như địa chỉ, số điện thoại, lịch sử mua hàng… Mặc dù trong trường hợp này, client có thể chỉ cần id, nameemail.

fetch('/users/123')
  .then(response => response.json())
  .then(data => {
    console.log(data);
    // Dữ liệu nhận được có thể bao gồm:
    // {
    //   "id": "123",
    //   "name": "Nguyen Van A",
    //   "email": "a.nguyen@example.com",
    //   "address": "123 ABC Street",
    //   "phone": "090xxxxxxx",
    //   "purchaseHistory": [...]
    // }
    const userId = data.id;
    const userName = data.name;
    const userEmail = data.email;
    console.log("User ID:", userId);
    console.log("User Name:", userName);
    console.log("User Email:", userEmail);
  })
  .catch(error => {
    console.error("Lỗi khi gọi API:", error);
  });

Trong ví dụ này, client gửi một request GET đến endpoint /users/123. Khi nhận được response, client chỉ sử dụng id, nameemail, nhưng vẫn nhận về các trường address, phone, và purchaseHistory mà có thể không cần thiết trong trường hợp này (ví dụ về over-fetching).

Với GraphQL:

Client sẽ gửi một query đến một endpoint duy nhất (thường là /graphql) với nội dung như sau:

query GetUser {
  user(id: "123") {
    id
    name
    email
  }
}

Trong query này, client đã chỉ định rõ ràng rằng họ chỉ muốn nhận về id, nameemail của user có ID là “123”. Server sẽ chỉ trả về đúng những trường này trong response:

{
  "data": {
    "user": {
      "id": "123",
      "name": "Nguyen Van A",
      "email": "a.nguyen@example.com"
    }
  }
}

Anh em thấy rõ sự khác biệt chưa? GraphQL cho phép client “nói” chính xác những gì mình muốn, không phải nhận cả mớ dữ liệu mà có khi chỉ dùng một phần.

So sánh GraphQL và REST API:

Để có cái nhìn tổng quan hơn, chúng ta hãy cùng so sánh GraphQL và REST API dựa trên một số khía cạnh quan trọng:

Cách truy xuất

REST API: Để lấy thông tin về một bài viết (ví dụ: tiêu đề, nội dung) và thông tin về tác giả của bài viết đó (ví dụ: ID, tên), thường bạn sẽ cần thực hiện hai request riêng biệt. Đầu tiên, bạn gọi endpoint /posts/{id} để lấy thông tin bài viết, sau đó bạn phải “bóc tách” ID của tác giả từ response này và gọi tiếp endpoint /users/{authorId} để lấy thông tin tác giả.

GraphQL: Bạn chỉ cần gửi một query duy nhất đến endpoint /graphql để yêu cầu tất cả thông tin bạn cần.

query GetPostWithAuthor {
  post(id: "1") {
    title
    body
    author {
      id
      name
    }
  }
}

GraphQL server sẽ tự động “liên kết” dữ liệu từ các nguồn khác nhau và trả về cho bạn một response duy nhất chứa đầy đủ thông tin.

Số lượng endpoint

REST API: Mỗi resource (ví dụ: người dùng, sản phẩm, đơn hàng) thường có một tập hợp các endpoint riêng để thực hiện các thao tác CRUD (Create, Read, Update, Delete). Ví dụ: /users (GET, POST), /users/{id} (GET, PUT, DELETE), /products, /orders… Số lượng endpoint có thể tăng lên rất nhiều khi ứng dụng phức tạp hơn.

GraphQL: Thông thường, chỉ có một endpoint duy nhất, thường là /graphql. Client sẽ gửi các query và mutation khác nhau đến endpoint này để tương tác với API.

Dữ liệu trả về

REST API: Khi bạn gọi một endpoint như /users/123, server có thể trả về rất nhiều thông tin về người dùng này, bao gồm cả những thông tin mà ứng dụng của bạn không thực sự cần đến trong ngữ cảnh hiện tại (ví dụ: địa chỉ, số điện thoại, lịch sử hoạt động…).

GraphQL: Client có quyền “chọn” chính xác những trường dữ liệu mà họ muốn nhận về. Ví dụ, nếu bạn chỉ cần hiển thị tên và email của người dùng, bạn chỉ cần yêu cầu hai trường đó trong query.

Over/Under-fetching

REST API (Over-fetching): Bạn muốn hiển thị danh sách các sản phẩm trên trang chủ, nhưng endpoint /products lại trả về toàn bộ thông tin chi tiết của từng sản phẩm, bao gồm cả mô tả dài, thông số kỹ thuật phức tạp mà bạn chưa cần đến. Điều này gây lãng phí băng thông.

REST API (Under-fetching): Để hiển thị thông tin chi tiết của một đơn hàng, bạn cần lấy thông tin đơn hàng từ endpoint /orders/{id}, sau đó bạn lại phải lấy thông tin của khách hàng đặt đơn hàng đó từ endpoint /users/{customerId}. Bạn phải thực hiện nhiều request để có đủ dữ liệu.

GraphQL: Bạn có thể tạo một query để lấy danh sách sản phẩm chỉ với tên và giá, hoặc một query khác để lấy thông tin đơn hàng và thông tin khách hàng trong cùng một lần request.

Versioning

REST API: Khi có những thay đổi lớn trong API, ví dụ như thay đổi cấu trúc response của endpoint /users/, bạn thường phải tạo ra một phiên bản mới của API, ví dụ như /api/v2/users/, để đảm bảo tính tương thích ngược cho các ứng dụng cũ. Việc quản lý nhiều phiên bản API có thể trở nên phức tạp.

GraphQL: Nhờ khả năng client tự chọn dữ liệu, những thay đổi nhỏ ở backend (ví dụ: thêm một trường mới vào một type) thường không ảnh hưởng đến các query hiện có. Nếu có những thay đổi lớn hơn, bạn có thể đánh dấu các trường cũ là deprecated (không khuyến khích sử dụng nữa) trong schema thay vì phải tạo ra một phiên bản API hoàn toàn mới.

Kiểu dữ liệu

REST API: Mặc dù dữ liệu thường được truyền tải dưới dạng JSON, nhưng không có một cơ chế ràng buộc kiểu dữ liệu nào ở cấp độ API. Client và server cần tự “thỏa thuận” và “hiểu” cấu trúc và kiểu dữ liệu của JSON response.

GraphQL: Schema được xây dựng với một hệ thống kiểu dữ liệu mạnh mẽ. Mỗi field trong schema sẽ có một kiểu dữ liệu rõ ràng (ví dụ: String, Int, Boolean, hoặc các type do bạn định nghĩa). Điều này giúp phát hiện lỗi sai kiểu dữ liệu ngay trong quá trình phát triển và cung cấp khả năng kiểm tra tính hợp lệ của dữ liệu mạnh mẽ.

Phản hồi lỗi

REST API: Lỗi thường được báo hiệu thông qua HTTP status codes. Ví dụ, lỗi “không tìm thấy tài nguyên” thường trả về status code 404, lỗi server thường trả về 500. Mặc dù status code cho biết loại lỗi, nhưng đôi khi không cung cấp đủ thông tin chi tiết về nguyên nhân gây ra lỗi.

GraphQL: Thông tin lỗi được trả về một cách chi tiết và cụ thể trong phần errors của response, cùng với dữ liệu (nếu có). Điều này giúp client dễ dàng xác định chính xác vấn đề và xử lý nó một cách phù hợp. Ví dụ, một lỗi “không tìm thấy user với ID ‘abc'” có thể được trả về kèm theo thông tin về đường dẫn gây ra lỗi (path) và một mã lỗi cụ thể (code).

Tính linh hoạt

REST API: Nếu front-end cần một tập hợp dữ liệu khác so với những gì mà các endpoint hiện có cung cấp, họ thường phải yêu cầu backend developer tạo ra một endpoint mới hoặc điều chỉnh endpoint hiện có. Điều này có thể làm chậm quá trình phát triển.

GraphQL: Front-end developer có toàn quyền quyết định dữ liệu nào họ cần cho từng màn hình hoặc component. Họ có thể tạo các query tùy chỉnh mà không cần phải phụ thuộc vào backend developer tạo ra các endpoint cụ thể cho từng trường hợp.

Một quá trình so găng rất dài hơi phải không nào? Mình tổng hợp lại tại bảng này cho anh em dễ so sánh nhé.

Tính năng GraphQL REST API Ví dụ
Cách truy xuất Client chọn lọc dữ liệu chính xác cần Server trả về dữ liệu cố định cho mỗi endpoint REST: Để lấy thông tin một bài viết và tác giả, có thể cần 2 request: /posts/1/users/{authorId}

GraphQL: Chỉ cần 1 query: query { post(id: 1) { title body author { id name } } }
Số lượng endpoint Thường chỉ có một endpoint duy nhất (/graphql) Thường có nhiều endpoint khác nhau (/users, /products, /orders,…) REST: Mỗi resource (người dùng, sản phẩm, đơn hàng) thường có endpoint riêng.

GraphQL: Mọi thao tác đều gửi đến /graphql.
Dữ liệu trả về Chính xác những gì client yêu cầu Có thể thừa hoặc thiếu dữ liệu REST: Endpoint /users/123 có thể trả về cả địa chỉ, số điện thoại mà ứng dụng hiện tại không cần.

GraphQL: Client chỉ yêu cầu id, name, email, server chỉ trả về đúng 3 trường này.
Over/Under-fetching Giải quyết hiệu quả Vấn đề thường gặp REST (Over-fetching): Lấy danh sách sản phẩm, endpoint trả về cả mô tả chi tiết dù chỉ cần tên và giá.

REST (Under-fetching): Để hiển thị thông tin đơn hàng và thông tin khách hàng, cần gửi 2 request riêng biệt.

GraphQL: Client có thể yêu cầu chính xác tên và giá sản phẩm trong list, hoặc thông tin đơn hàng và thông tin khách hàng trong một query duy nhất.
Versioning Thường không cần versioning Thường cần quản lý versioning (/api/v1/users, /api/v2/users) REST: Khi thay đổi cấu trúc response của /users, thường phải tạo /users/v2.

GraphQL : Client chỉ cần điều chỉnh query để phù hợp với schema mới, các query cũ vẫn có thể hoạt động nếu các trường cũ vẫn tồn tại.
Kiểu dữ liệu Kiểu tĩnh (strong typing) Linh hoạt (thường là JSON) REST: Client và server cần tự “hiểu” cấu trúc và kiểu dữ liệu của JSON response.

GraphQL: Schema định nghĩa rõ ràng kiểu dữ liệu của từng field (String, Int, Boolean, User, Post…).
Phản hồi lỗi Chi tiết và cụ thể trong response Thường dựa vào HTTP status codes (4xx, 5xx) REST: Lỗi “không tìm thấy user” thường chỉ trả về status code 404.

GraphQL: Response có thể chứa thông tin chi tiết về lỗi.
Ví dụ: {"errors": [{"message": "User with ID 'abc' not found", "path": ["user"], "extensions": {"code": "NOT_FOUND"}}]}.
Tính linh hoạt Rất linh hoạt cho client Ít linh hoạt hơn cho client REST: Nếu front-end cần thêm một trường dữ liệu mới, backend developer có thể phải chỉnh sửa endpoint hoặc tạo endpoint mới.

GraphQL: Front-end developer có thể tự do yêu cầu dữ liệu mình cần mà không cần backend phải tạo thêm endpoint mới.

Ưu điểm của GraphQL. Khi nào nên áp dụng vào dự án?

Dựa trên những so sánh trên, chúng ta có thể thấy GraphQL mang lại nhiều ưu điểm đáng chú ý:

Giải quyết vấn đề Over-fetching và Under-fetching: Giúp ứng dụng nhanh hơn, tiết kiệm băng thông và tài nguyên.

✅ Đặc biệt hữu ích cho các ứng dụng di động và web có băng thông hạn chế hoặc cần tải dữ liệu nhanh chóng. Phù hợp với các giao diện người dùng phức tạp đòi hỏi nhiều loại dữ liệu khác nhau (ví dụ: trang dashboard tổng hợp nhiều thông tin từ các nguồn khác nhau).

Một endpoint duy nhất: Đơn giản hóa việc quản lý và phát triển API.

Thuận tiện cho việc quản lý API, giảm số lượng request từ client, đơn giản hóa cấu trúc backend. Thích hợp cho các ứng dụng lớn với nhiều loại tài nguyên (ví dụ: một hệ thống e-commerce với hàng ngàn sản phẩm, đơn hàng, người dùng…).

Schema mạnh mẽ và hệ thống kiểu tĩnh: Tăng cường khả năng kiểm tra lỗi trong quá trình phát triển, cung cấp tài liệu API tự động và chính xác (thường thông qua các công cụ như GraphiQL hoặc Apollo Sandbox), hỗ trợ tốt cho các dự án lớn với nhiều nhà phát triển tham gia.

✅ Cải thiện khả năng kiểm tra lỗi trong quá trình phát triển, cung cấp tài liệu API tự động và chính xác, hỗ trợ tốt cho các dự án lớn với nhiều nhà phát triển tham gia (giúp các team dễ dàng hiểu và sử dụng API của nhau).

Trải nghiệm phát triển tốt hơn: Front-end developer có thể dễ dàng khám phá và yêu cầu dữ liệu cần thiết mà không cần phải đoán xem endpoint nào sẽ trả về những gì.

✅ Giúp front-end developer dễ dàng khám phá và yêu cầu dữ liệu cần thiết, tăng tốc độ phát triển ứng dụng (ví dụ: khi xây dựng UI mới, front-end có thể nhanh chóng xác định query cần thiết mà không cần chờ backend cung cấp endpoint).

Hỗ trợ real-time với Subscriptions: Mở ra khả năng xây dựng các ứng dụng tương tác cao như chat, thông báo trực tiếp, bảng điều khiển thời gian thực.

✅ Cần thiết cho các ứng dụng yêu cầu cập nhật dữ liệu liên tục (ví dụ: ứng dụng chat, sàn giao dịch chứng khoán, hệ thống giám sát…).

Khả năng phát triển API nhanh chóng: Với cách tiếp cận schema-first development (xây dựng schema trước khi hoàn thiện backend), việc định hình API trở nên nhanh chóng và dễ dàng hơn.

✅ Phù hợp với các dự án cần nhanh chóng đưa sản phẩm ra thị trường, cho phép định hình API trước khi hoàn thiện backend (ví dụ: trong giai đoạn MVP, team có thể nhanh chóng thống nhất về cấu trúc dữ liệu cần thiết).

Khi nào nên sử dụng GraphQL?

  • Ứng dụng web và di động phức tạp với nhiều loại dữ liệu và nhu cầu hiển thị khác nhau.
  • Yêu cầu tối ưu hóa hiệu suất và giảm thiểu lượng dữ liệu truyền tải.
  • Front-end team có nhu cầu kiểm soát dữ liệu họ nhận được.
  • Ứng dụng cần khả năng real-time.
  • Các dự án lớn với nhiều nhà phát triển và cần một API có cấu trúc rõ ràng.
  • Khi bạn muốn có tài liệu API tự động và luôn cập nhật.

Nhược điểm của GraphQL. Tốt nhưng chưa là tất cả

Bên cạnh những ưu điểm, GraphQL cũng có một số nhược điểm cần cân nhắc:

Độ phức tạp ban đầu: Việc thiết lập và học hỏi GraphQL có thể đòi hỏi nhiều thời gian và công sức hơn so với REST, đặc biệt đối với những người mới bắt đầu.

➡️ Có thể không cần thiết cho các dự án nhỏ, đơn giản hoặc các API chỉ có một vài chức năng cơ bản. Đội ngũ phát triển cần đầu tư thời gian để học và làm quen với GraphQL.

Khả năng truy vấn phức tạp: GraphQL cho phép client gửi các truy vấn rất phức tạp, có thể gây ra vấn đề về hiệu suất và bảo mật cho server nếu không được xử lý cẩn thận (ví dụ: một query lồng nhau quá nhiều cấp có thể làm quá tải database).

➡️ Cần cân nhắc kỹ lưỡng đối với các hệ thống có yêu cầu bảo mật và kiểm soát tài nguyên nghiêm ngặt, vì các truy vấn quá phức tạp có thể gây ra vấn đề về hiệu suất. Cần có các biện pháp phòng ngừa như giới hạn độ sâu của query, cost analysis…

Vấn đề về caching: Việc triển khai caching hiệu quả với GraphQL có thể phức tạp hơn so với REST vì chỉ có một endpoint duy nhất. Với REST, bạn có thể tận dụng HTTP caching dựa trên URL.

➡️ Có thể là một thách thức đối với các ứng dụng có yêu cầu caching phức tạp và hiệu suất cực cao. Cần có các chiến lược caching phù hợp ở cả client và server (ví dụ: sử dụng thư viện caching GraphQL client như Apollo Client, hoặc implement caching layer ở server).

Rate limiting và bảo mật: Việc bảo vệ một endpoint duy nhất khỏi các tấn công và lạm dụng (ví dụ: tấn công DoS) có thể đòi hỏi các giải pháp tùy chỉnh thay vì tận dụng các cơ chế sẵn có của HTTP như với REST.

➡️ Cần có các giải pháp tùy chỉnh để xử lý rate limiting và bảo mật cho một endpoint duy nhất, đặc biệt đối với các API công khai.

Không phù hợp cho mọi trường hợp: Đối với các API rất đơn giản, chỉ thực hiện các thao tác CRUD cơ bản trên một vài tài nguyên, REST có thể là một lựa chọn tốt hơn với chi phí triển khai thấp hơn và dễ hiểu hơn.

➡️ Các API rất đơn giản, chỉ thực hiện các thao tác CRUD cơ bản trên một vài tài nguyên có thể được phục vụ tốt hơn bằng REST với chi phí triển khai thấp hơn.

Khi nào không nên sử dụng GraphQL?

Các API rất đơn giản, chỉ thực hiện các thao tác CRUD cơ bản. Đội ngũ phát triển đã có kinh nghiệm sâu rộng với REST và không có nhu cầu chuyển đổi, đặc biệt khi thời gian và nguồn lực có hạn. Yêu cầu về caching rất phức tạp và khó thực hiện hiệu quả với GraphQL. Các ứng dụng có tài nguyên hạn chế và không đủ nguồn lực để đầu tư vào việc học và triển khai GraphQL. Các hệ thống legacy mà việc tích hợp GraphQL là quá phức tạp và tốn kém.

Lời kết

Tóm lại, GraphQL và REST API là hai cách tiếp cận khác nhau để xây dựng API. GraphQL mang lại sự linh hoạt và hiệu quả cao hơn trong việc truy xuất dữ liệu, đặc biệt cho các ứng dụng phức tạp. Tuy nhiên, nó cũng có những thách thức riêng về độ phức tạp và caching.

Lời khuyên của mình là anh em nên cân nhắc kỹ lưỡng yêu cầu cụ thể của dự án, kinh nghiệm của team và nguồn lực hiện có để đưa ra quyết định lựa chọn công nghệ phù hợp nhất. Đừng ngại thử nghiệm GraphQL cho những dự án tiềm năng, nó có thể mang lại những lợi ích bất ngờ đấy!

Nếu anh em có bất kỳ câu hỏi nào về GraphQL, đừng ngần ngại để lại bình luận bên dưới nhé. Hẹn gặp lại ở những bài viết tiếp theo.

SiuCode – Vừa code vừa siuuuu 🚀

 

You may also like

Mục lục