Cách sài GraphQL – Góc nhìn Frontend
Chào các bác ,
Như đã nói, em sẽ tập trung vào GraphQL trong mây bài gần đây. Trong bài viết ngắn ngủi này, em muốn giới thiệu tới các bác về cách sử dụng GraphQL duới góc nhìn của một dev frontend – GraphQL sử dụng làm sao, khác khỉ gì so với thằng Restful quen thuộc ? Cùng tìm hiểu nhé.
Demo – Playground
Ok bắt đầu vào câu chuyện nhé
Frontend: Anh ơi, gửi em URL API đê em làm Frontend nhanh không sếp chửi
Backend: Nè chú API: http://cuthanh.com:2700/graphql Document: http://cuthanh.com:2700/graphiql
Thế là các bác mò mẫm vào Document xem nó như thế nào
Em đã xây dựng một GraphQL API sẵn cho các bác chơi bời. GraphQL này xây dựng để thay thế cho Restful api mà mình đã nói cách xây dựng trọng loạt bài Xây dựng hệ thống gợi ý
http://cuthanh.com:2700/graphiql
Các bác vào để xem document luôn nhé
GraphQL sẽ chia thành 2 phần chính
- Query Các câu lệnh lấy dữ liệu (tương tự method GET trong RestFul API)
- Mutation Các câu lệnh để thêm/sửa dữ liệu (tuơng tự method POST/PUT/DELETE trong RestFul API)
- Subscription (Ơ dm thằng này không biết đếm à
) Thực ra cái này có chức năng kiểu như Emitter như bài viết bữa trước em đã nói. Client nói với Server rằng “này khi nào có thêm thăng Xmen mới thì báo cho tao nhé
“. Vậy là Client đang lắng nghe server với sự kiện Thêm Xmen mới, do đó, mỗi khi có Xmen mới được thêm vào thì Server sẽ gửi Data cho Client. Bá đạo chưa, Restful làm gì có chức năng như vầy đúng không các bác. Tuy nhiên chức năng này mới chỉ đang thai nghén, và chưa được hỗ trợ chính thức
. Do đó các bác ráng đợi nhé
Query
Như trong document, query có 2 lệnh là
Xmens
– lấy toàn bộ danh sách các XmenXmen(UID: Int)
– lấy thông tin của một Xmen cụ thể
Ok theo ứng dụng của chúng ta, mới vào chúng ta cần danh sách Xmen để show off ra homepage. Mà việc show off thì chỉ cần Avatar đỉnh + một cái tên cool ngầu là được (Công thức tán gái trên facebook )
Query đơn giản
Viết query
{ Xmens { name avatar } }
Ok, giờ ấn nút “Play” để “Chơi” thứ nó nhé
{ "data": { "Xmens": [ { "name": "Wolverine", "avatar": "images/wolverine.jpg" }, { "name": "Magneto", "avatar": "images/magneto.jpg" }, { "name": "Mystique", "avatar": "images/mystique.jpg" }, { "name": "Professor X", "avatar": "images/professorx.jpg" }, { "name": "Quick Silver", "avatar": "images/quicksilver.jpg" } ] } }
Kiểu vậy. Má data của em các bác làm nhoặng trong đấy nên em chỉ lấy một ít thôi . Thấy phê chưa, query thì dễ nhìn
. Result trả về thì gần như y chang query ngoài việc gắn thêm data vào
.
Query với tham số (argument)
Ok, bây giờ giả sử User sẽ click vào một Xmen nào đó, chúng ta sẽ phải router tới trang cá nhân của user đó và đương nhiên, phải fetch data của user đó về rồi
Vậy ta sẽ sử dụng thằng query thứ 2 Xmen(UID: Int): Xmen
Một lệnh Query viết ở Document sẽ ở dạng prototype như sau: Tên_Query(Tên_argument1: Kiểu_argument1, Tên_argument2: Kiểu_argument2):Kiểu trả về
Với document cho thằng Xmen(UID: Int): Xmen
chúng ta thấy được tên query là Xmen
, Argument
chúng ta cần truyền vào là UID
, ở kiểu Int
. Thằng này sẽ trả về một kiểu gọi là kiểu Xmen
. Còn kiểu Xmen
như thế nào các bác ấn vào ở Document mà tự nghiền ngẫm đi nhé .
{ Xmen(UID:1) { name avatar like { name avatar } } }
Các bác chạy thử xem Kết quả làm sao nhé
Query trong query
Có một điểm đáng chú ý ở đây là field like
sẽ trả về array(Xmen)
, nghĩa là thằng like
này lưu giữ toàn bộ thông tin những Xmen mà nhân vật Wolverine của chúng ta yêu thích. Kiểu trả về của nó sẽ y chang như kiểu mà Xmen(UID: 1)
trả về. Mẹ thằng này nói khó hiểu vl , ví dụ thử coi
{ Xmen(UID:1) { name avatar like { name like { name avatar like { name } } } } }
Câu lệnh trên nghĩa là sao các bác.
- Nghĩa là nó sẽ lấy thông tin
(name, avatar, like)
của Xmen có UID bằng 1 (Thằng Wolverine đấy) - Tiếp theo ta sẽ lấy thông tin
(nam, like)
của tất cả những thằng Xmen được Wolverine yêu thích(VD có n thằng trả về)
- Tiếp nữa, chúng ta lại lấy thông tin
(name, avatar, like)
của tất cả những thằng được Xmen được Wolverine yêu thích yêu thích(Vd có m thằng trả về)
- Cuối cúng, chúng ta lấy thông tin
(name)
của tất cả những thằng Xmen được Wolverine yêu thích yêu thích yêu thích.(Vd có k thằng trả về)
- Done! (response) trả về gì các bác tự xem nhé.
Haha fuck your mind =))
Thử tượng tượng xem, nếu chúng ta dùng Rest thì phải làm sao. Chúng ta query /1
. Đợi response
trả về, lấy field like
query tiếp n thằng nữa. Ứng với mỗi n ta sẽ query m lần nữa. Cuối cùng, ứng với mỗi m ta phải query k lần nữa.
Vậy là tổng cộng 1 + n*m*k lần phải query.
Đặc biệt hơn nữa, các bác phải đợi thằng trước trả kết quả rồi mới có thể query thằng sau. Trong khi với GraphQL, fortunately, chúng ta chỉ cần query đúng 1 lần. Thêm nữa, với Restful, mỗi lần query sẽ trả về full field của một xmen (_id, UID, name, avatar, date, like, _v)
. Với GraphQL chúng ta chỉ cần lấy field nào chúng ta cần. Ez
Tới đây bạn đã thấy thằng GraphQL này bá đạo cỡ nào chưa . Với ví dụ trên thôi, theo mình GraphQL đã chạy nhanh hơn Restful API mấy trăm lần rồi.
Batch Request
Chưa hết, GraphQL còn hỗ trợ Batch Request (cái này chúng bên Restful chúng ta cũng làm được nhưng mệt mỏi lắm, do đó các bác sẽ rất ít được gặp nó)
Batch Request là gì, nghĩa là request nhiều query cúng một lúc. Ví dụ trong ứng dụng của chúng ta, có một trang cần cả thông tin của 1 Xmen cụ thể, cần cả thông tin của danh sách Xmen nữa, giờ sao?
{ Xmens { name avatar } Xmen(UID:1) { name avatar like { name } } }
Aliases
Vâng, với việc hỗ trợ Batch Request luôn thì chúng ta lại nảy sinh một vài vấn đề nữa (Cái này gọi là nợ mẹ đẻ nợ con )
Nếu chúng ta cần Query lấy thông tin của Xmen có UID 1
và Xmen có UID là 2
thì sao
{ Xmen(UID:1) { name avatar like { name } } Xmen(UID:2) { name avatar like { name } } }
Các bác thử copy rồi ấn nút “Chơi” thử coi
Lỗi đúng không. Hehe . Vì khi trả data về, server tương đương lắp data vào những field chúng ta query. Nhưng trong câu Query trên, có 2 field Xmen ở cùng cấp nên nó đị conflict (mâu thuẫn với nhau), làm sao có 2 field trong cùng một object (Javascript) được, đúng không các bác
. Dù sao, các bác cũng query câu trên thử đi, server sẽ ném vào mặt các bác câu lỗi
Fields \"Xmen\" conflict because they have differing arguments.
Use different aliases on the fields to fetch both if this was intentional.
Nghĩa là chúng ta nên đặt tên khác/bí danh (aliases) để giải quyết vấn đề. Đặt aliases làm sao? Xem bên dưới
{ ThangA: Xmen(UID:1) { name avatar like { name } } ThangB: Xmen(UID:2) { name avatar like { name } } }
Đấy, vậy là lại có Response ngon lành , chả lỗi lầm khỉ gì cả.
Nâng cao – Operation Name, Directive, Variable
Ok, giờ nâng cao thêm xíu nữa nhể.
query getXmen($input:Int!, $getLike: Boolean!) { Xmen(UID:$input) { name like @include(if: $getLike) { UID name avatar date } } }
{ "input": 1, "getLike": false }
Đầu tiên, ta thấy được dòng query getXmen($input:Int!, $getLike: Boolean!)
khá lạ huh ? Cái này là định nghĩa một function các mẹ ạ (hay đại loại kiểu vậy), thằng GraphQL gọi nó là Operation, em thích gọi là Function
. Okey, function thì phải có input – các argument. Và argument bắt đầu bằng kí hiệu $ (giống định nghĩa variable trong PHP nhỉ).
Mục đính viết function vào đấy làm gì? Reusable. Function giúp chúng ta chạy một đống câu lệnh bên trong mà mỗi lần sử dụng không phải viết lại từng câu lệnh và đối với GraphQL cùng để là dễ Debug nữa (Document của GraphQL viết vậy, em vẫn méo thấy dễ chỗ nào cả vì cũng chưa gặp Bug).
Ta định nghĩa Query ở trên, giá trị variable ở một nơi khác. Nó giống như định nghĩa một function, còn mỗi khi gọi nó ta lại pass argument khác nhau tuỳ trường hợp vậy. Giúp code của các bác dễ quản lý hơn.
Điều đặc biệt thứ 2 là đoạn like @include(if: $getLike)
Đoạn này có nghĩa là chỉ Get field like
nếu giá trị $getLike
là true
. Khá hay phải không? Và ngược lại với @include
chúng ta có @skip(if: $variable_in_bool_type)
, thằng @skip
này hoạt động ngược lại với thằng @include
. Nói chung chỉ cần biết một thằng thôi là đủ rồi, nhẹ não .
Ngoài các kiểu Query mà mình đã nói bên trên, còn một số định nghĩa khác về Fragments, Meta Type nữa. Mình sẽ đề cập vào lần sau tại vì 2 thằng này chỉ thực sự phát huy công dụng tốt khi chúng ta làm việc với React/ReactNative và định nghĩa Type trong GraphQL phức tạp thôi .
Tạm thời vậy đã.
Mutation
Một ứng dụng, trong thực tế, sẽ không chỉ có việc Fetch data từ server về. Nó cần phải có những thay đổi, thêm bớt và gửi lên server nữa. Để làm việc này, GraphQL tách ra thành nhánh Mutation.
Có một điều quan trọng em phải nhắc các bác trước. Mutation luôn luôn phải đặt trong một function.
mutation addAXmen($input: XmenInput!) { addXmen(input:$input) { name avatar } }
{ "input": { "name": "Cu's Thanh's", "avatar": "https://graph.facebook.com/100003217152910/picture?type=large" } }
Cách để thêm một Xmen Cu’s Thanh’s vào data của chúng ta. Các bác chú ý một điều nữa là nếu mutation nào chúng ta sử dụng mà cần argument not null (Có dấu !
ở đằng sau đấy), thì khi định nghĩa function, chúng ta cũng phải cần thêm dấu !
vào định nghĩa kiểu của variable nhé. Một dấu !
thôi mà cũng em điên đầu mấy tiếng đấy
Một mutation thì có thể trả về kết quả. Còn kết quả kiểu gì thì các bác tự đọc document đi nhá. Của em là kiểu Xmen, do đó, chúng ta cũng thể nói với GraphQL là, “bố chỉ cần những field x, field y thôi nhé con trai… ”
Subcription
Phần này em chưa code, và GraphQL cũng chưa có phương án giải quyết chính thức cho thằng này . Vì thế các bác đợi bài sau nhé.
Kết bài
Qua đây, dưới góc nhìn của Frontend thì thấy sao các bác. Đối với em thì thấy sướng quá, query cực kì thích, trực quan. Lại còn không phải xây dựng một đống query lằng nhằng, thằng này đợi thằng kia nữa chứ
Document thì khá là phê (em đánh giá là 8/10).
Về phần Mutation thì đúng thực là hơi nhằng hơn so với Restful một xíu, nhưng không sao, vẫn ngon lành ha .
Từ đấy, thấy rằng GraphQL thực sự là một bước tiến so với Restful. Nó cực kì tiết kiệm thời gian cho frontend, và cả tài nguyên cho User nữa (không phải vêu mồm chờ response ).
Còn các bác, các bác thấy GraphQL có thực sự nuột như em nói không, còn chỗ nào sida? Comment bên dưới nhé!
Comments
2 Comments
đọc xong méo hiểu cái mẹ gì luôn tía à
Tiếng miên đấy con ơi =]]
Leave a Comment