GraphQL – Con quái thú chưa được thuần chủng trong thế giới web

Kể từ khi Facebook cho ra mắt vào năm 2015 thì cái tên GraphQL được cộng đồng developer nhắc đến khá nhiều mỗi khi xây dựng API. Vậy GraphQL là gì? Nó có sức mạnh như thế nào mà được mọi người quan tâm đến như thế. Mời các bạn cùng mình tìm hiểu về nó qua bài viết dưới đây.

Bài viết này được chia thành hai phần: trong phần đầu tiên, chúng ta sẽ tìm hiểu GraphQL là gì, các tính năng, ưu nhược điểm của GraphQL , sau đó trong phần thứ hai, chúng ta sẽ bắt tay vào build GraphQL server đơn giản trên nền tảng NodeJS và Express.

 

1. GraphQL là gì?

GraphQL là ngôn ngữ thao tác và truy vấn dữ liệu nguồn mở cho API, cung cấp cho client 1 cách thức dễ dàng để request chính xác những gì họ cần, giúp việc phát triển API dễ dàng hơn theo thời gian.

GraphQL được Facebook phát triển nội bộ vào năm 2012 trước khi phát hành công khai vào năm 2015.

 

2. Tính năng cơ bản

2.1 Queries

Query được hiểu đơn giản là  hành động đọc, lấy dữ liệu từ server để trả lại data cho phía client. Dưới đây là một ví dụ về một query đơn giản lên server để lấy toàn bộ các thông tin id, name, category của Books và response tương ứng:

2.1.1 Field

Hiểu một cách đơn giản thì “field” là thành phần của một object mà chúng ta muốn thu được từ server khi chúng ta thực hiện các thao tác như query,.. Trong ví dụ bên trên thì các field tương ứng là: id, name và category.

2.1.2 Arguments

Giống như các hàm trong các ngôn ngữ lập trình query có thể có các đối số có thể là bắt buộc có thể là tuỳ chọn để query chính chính xác những thông tin cần thiết

2.1.3 Variables

Bên cạnh tham số, một query cũng có thể có các biến để thao tác truy vấn với dữ liệu một cách ngắn gọn hơn. Các biến có thể được khai báo sau query hoặc mutation và được truyền dưới dạng đối số cho một hàm và bắt đầu bằng $.

Có thể gán giá trị mặc định cho nó:

Giống với các tham số thì biến có thể là bắt buộc hoặc tuỳ chọn. Đối với những biến bắt buộc cần thêm ! khi định nghĩa biến (bao gồm cả lược đồ schema)

2.1.4 Aliases

Aliases được tạo ra nhằm giúp client có thể request dữ liệu từ cùng một field với các tham số khác nhau trong cùng 1 request.

Trong ví dụ trên hai thuộc tính book sẽ xung đột và không thể thực hiện được request fetch các thông tin theo id nếu như chúng ta không sử dụng Aliases để đặt tên cho nó.

2.1.5 Fragments

Fragments cho phép chúng xây dựng những tập hợp các field có khả năng tái sử dụng tránh tình trạng lặp lại trong nhiều request khác nhau. Chúng ta có thể sử dụng bằng cách khai báo 1 fragment sau đó gọi nó trong các query như ví dụ sau đây.

Thành phần của 1 fragment bao gồm:

  • Name: Tên của fragment.
  • Object: Loại đối tượng fragment sử dụng(Trong ví dụ bên trên là Book).
  • Field: các field dữ liệu tái sử dụng.

2.1.6 Directives

Directives cho phép ta thay đổi cấu trúc query một cách linh hoạt sử dụng các biến. GraphQL có 2 directive:

  • @include sẽ include một field hay fragment khi tham số if là true.
  • @skip sẽ bỏ qua một field hay fragment khi tham số if là false.

Cả hai directive nhận tham số kiểu boolean.

2.2 Mutation

Trong GraphQL, việc thực hiện thêm mới hay sửa đổi dữ liệu với server được thực hiện thông qua mutation. Nó tương ứng như các thao tác  POST, PUT và DELETE trong REST API.

Ví dụ dưới đây là cú pháp mutation cho phép chúng ta thêm mới bản ghi Book vào database với tên hàm là createBook

Một điểm khác biệt quan trọng giữa các mutation và query là các mutation được thực hiện nối tiếp để đảm bảo tính toàn vẹn của dữ liệu, trong khi các query được thực hiện song song.

2.3 Subscription

Trong quá trình xây dựng và phát triển bất kì 1 sản phẩm nào một trong những yêu cầu quan trọng đó là realtime, để có thể kết nối đến máy chủ để có được thông tin về các event ngay lập tức. Trong trường hợp này, GraphQL cung cấp các khái niệm gọi là subscriptions.

Khi 1 client subscriptions một event, nó sẽ bắt bắt đầu tạo và giữ các kết nối đến server. Bất cứ khi nào sự kiện đó diễn ra, server lắng nghe và gửi lại những thông tin đến client. Không giống như Query và Mutation, nó đi theo kiểu như “request-response-cycle”, nó sẽ subscriptions đại diện của luồng dữ liệu được gửi đến client.

Subscriptions được viết bằng cách sử dụng cú pháp như Query và Mutation. Ví dụ sau đây là kết quả sau khi subscription thành công sự kiện addBook

Sau khi client gửi một subscription đến server, 1 kết nối sẽ được mở giữa chúng. Sau đó bất cứ khi nào có 1 mutation được tạo ra bởi một bookCreated, server sẽ gửi thông tin về người dùng đó đến client.

3. GraphQL Clients

GraphQL Client được được phát triển nhằm ngoài việc tối ưu các tính năng của graphql mà chúng còn bổ sung thêm nhiều module mới giúp client có nhiều lựa chọn hơn cho dự án của mình. Một số tiện ích nổi bật mà nhiều GraphQL client đã phát triển như:

  • Hỗ trợ tính năng cache giúp tối ưu request.
  • Quản lý giao diện người dùng, GraphQL schema thuận tiện.
  • Hỗ trợ các biến môi trường.
  • Quản lý lịch sử request.
  • Miễn phí và mã nguồn mở.

Các Client GraphQL phổ biến trong đang được sử dụng rộng rãi hỗ trợ nhiều nền tảng khác nhau ví dụ như Apollo ClientRelay,..

4. GraphQL Servers

GraphQL servers bao gồm 2 phần chính:

  • Schema: Schema được hiểu như một bản mô tả về những thứ mà server có khả năng cung cấp. Nó như một hợp đồng giữa client và server, đảm bảo các yêu cầu về dữ liệu luôn được đáp ứng.
  • Resolver: Hiểu đơn giản thì nó như là một function hoặc method xử lý dữ liệu cho các field khi nhận được yêu cầu từ phía client.

Mỗi khi server nhận được yêu cầu, quá trình xử lý sẽ diễn ra như sau:

  • Phân tích.
  • Xác định hoạt động để thực hiện.
  • Xác thực yêu cầu và trả lại lỗi nếu không thành công.
  • Thực hiện thao tác (query/mutation/subscription).

5. GraphQL vs REST

GraphQL

REST

Ngôn ngữ truy vấn API

Là một khái niệm, 1 loại kiến trúc định nghĩa một số ràng buộc, quy tắc cần tuân theo khi thiết kế web service

Chỉ sử dụng 1 endpoint duy nhất và client có thể quyết định lấy những loại dữ liệu nào mà họ cần.

Sử dụng nhiều endpoint khác nhau để truy vấn, thông thường mỗi endpoint sẽ trả về resource duy nhất.

Kiến trúc hướng tới client.

Kiến trúc hướng tới phía server

Không có cơ chế cache tích hợp sẵn mà phải sử dụng các thư viện ngoài

Uses caching automatically

Không hỗ trợ API version

Hỗ trợ API version

Định dạng dữ liệu trả về JSON

Định dạng dữ liệu trả về  XML, JSON, and YAML

6 Ưu và nhược điểm

Ưu điểm:

  • Tốc độ: So với REST, GraphQL có tốc độ khá nhanh bởi vì nó chỉ lấy chính xác những dữ liệu mà client yêu cầu.
  • Chỉ sử dụng 1 endpoint duy nhất.
  • Versioning: GraphQL giải quyết được vấn đề liên quan đến phiên bản của API. Chúng ta có thể thêm các fields và types bất kỳ lúc nào mà không ảnh hưởng đến các truy vấn hiện có.
  • ..

Nhược điểm:

  • Status 200: GraphQL luôn trả về trạng thái 200 dù truy vấn đó có thành công hay không, điều này gây thêm khó khăn trong quá trình giải quyết lỗi.
  • No cache: GraphQL không hỗ trợ cache
  • Rất nhiều extension mã nguồn mở của GraphQL không tương thích và không thể hoạt động với REST API
  • ..

Link: https://graphql.org/

Link: https://hasura.io/learn/graphql/intro-graphql/introduction/

 Xây dựng GraphQL API Server với NodeJS và Express

Trong phần thứ 2 này mình sẽ hướng dẫn mọi người build một GraphQL API Server  sử dụng Apollo server trên nền tảng NodeJS và Express để có thể hiểu thấy rõ hơn được điểm mạnh của GraphQL

Bước 1: Tạo một project mới

  • mkdir graphql-server-demo
  • cd graphql-server-demo
  • npm init

Bước 2: Cài thư viện

Hiện nay có khá nhiều cách triển khai để xây dựng nên 1 graphql server ví dụ như: Apollo GraphQL, Hasura, Hot Chocolate… Và trong bài viết này mình sẽ sử dụng Apollo Server thiết lập. Ngoài ra chúng ta cũng cần cài đặt những thư viện cần thiết để tạo nên Graphql Server một cách dễ dàng nhất

  • express
  • mongoose
  • graphql
  • apollo-server-express
  • apollo-server-core
  • ws
  • graphql-ws
  • graphql-subscriptions
  • graphql-tools/schema

Ngoài ra bạn có thể cài thêm nodemon để thuận tiện hơn trong quá trình code. Sau khi cài đặt hoàn tất, dưới đây là toàn bộ những package cần thiết để thiết lập GraphQL API server sử dụng mongoDB

Bước 3: Tạo thư mục

Tiếp theo chúng ta cần xây dựng structure folder cho ứng dụng để quản lý project một cách dễ dàng hơn. Dưới đây là structure mình đã xây dựng sẵn các bạn có thể tham khảo:

Bước 4: Khởi tạo server

Đầu tiên chúng ta cần xây dựng 1 function để start server. Dưới đây là toàn bộ những config để khởi tạo ApolloServer v3 và connect tới mongoDB.

Chi tiết các bạn có thể tham khảo link này để thiết lập một cách dễ dàng nhất.

https://www.apollographql.com/docs/apollo-server/v3/getting-started

Bước 5: Thiết kế schema

Sau khi config server hoàn tất chúng ta cùng bắt đầu tiến hành define schema cho GraphQL

Cái field và kiểu dữ liệu tương ứng cho Author và Book

Bước 6: Thiết kế resolver

Để Graphql Server gửi về chính xác data mà phía client request thì ngoài define schema có một bước rất quan trọng đó là chúng cần define resolver. Hiểu nôm na đây là nó là mô tả hành vi mà server lắng nghe thực hiện truy vấn trong đến database để lấy thông tin data và trả lại cho client. Trong ví dụ này mình define các function xử lý cho các thao tác như query, mutation và subscription.

Function getAllBooks và getBookById thực hiện return data khi người dùng thao tác query.

Các function createBook, updateBookById, removeBook xử lý khi client thực hiện các hành động CREATE, UPDATE, DELETE cho những quyền sách. Và tương tư cho đối tượng là Author nhé..

Vừa rồi là toàn bộ quá trình để xây dựng nên một GraphQL server sử dụng Apollo Server. Sau đây chúng ta hãy cùng nhau test để xem kết quả/

Query

Ở phía client để nhanh chóng và thuận tiện mình sử dụng luôn studio apollographql sandbox để thao tác với dữ liệu thông qua server chúng ta vừa thiết lập ở trên. Đầu tiên với tính năng query

Ví dụ trên thông qua thao tác truy vấn chúng ta đã get ra được toàn bộ thông tin về những quyển sách cùng với thông tin tác giả tương ứng. Kết quả này cũng thể hiện rõ tính năng nổi bật của GraphQL đó là nó chỉ lấy chính xác những thông tin mà phía client yêu cầu

Mutation

Với nội dung kiến thức ở phần 1 chúng ta đã biết tính năng mutation cho phép client write data đến server thực hiện những hành động như(CREATE, UPDATE, DELETE)

Trong ví dụ này chúng ta thực hiện thêm 1 bản ghi chứa thông tin Author lên server.

Ngoài cách truyền variable giống như trong ví dụ các bạn có thể sử dụng arguments để gán trực tiếp các dữ liệu

Subscription

Nhắc lại một chút về tính năng subscription thì nó cho phép client đăng ký sự kiện với phía server bằng cú pháp subscription khi đó 1 kết nối sẽ được tạo. Và khi nào sự kiện xảy ra thì nó bắn thông tin để chúng ta có thể thực hiện các hành động khác một cách realtime.Trong ví dụ này mình sử dụng subscription để tạo kết nối lắng nghe tín hiệu khí client add một bản ghi mới vào server.

Trong resolver Subscription cần phải có 1 hàm để subscribe có message là ‘BOOK_CREATED’

Ngoài ra trong function addBook khi add thành công bản ghi vào database chúng ta cũng cần config 1 tín hiệu để subscription lắng nghe được sự kiện đã được bắn đi.

Và cuối cùng hãy cùng xem kết quả thông qua sandbox graphqlserver. Ở đây mình ở 2 tab để quan sát khi thực hiện thao tác mutation insert book vào database thì phía subscription ngay lập thức cũng bắn ra thông tin event đã được subscribe trước đó.

Và trên đây là là toàn bộ các bước để build nên graphql server sử dụng nền tảng nodejs và express.

Khá đơn giản đúng không, chúc các bạn thành công.

HieuPV

 

Comments

Let’s make a great impact together

Be a part of BraveBits to unlock your full potential and be proud of the impact you make.