[Backend] Sơ lược về GraphQL

GraphQL là gì? GraphQL có những ưu, nhược điểm gì? GraphQL liệu có thay thế được REST API? Khi nào nên dùng GraphQL?.. Hãy cùng mình đi tìm câu trả lời cho những câu hỏi trên qua bài viết này nhé!


1. GraphQL là gì?

GraphQL là một ngôn ngữ truy vấn cho API được Facebook ra mắt vào năm 2012 và open sourced năm 2015. Về định nghĩa chính xác của GraphQL, các bạn có thể dễ dàng tìm thấy trên trang chủ của GraphQL: graphql.org.

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

Hơi chung chung nhỉ, để làm rõ hơn, ta cùng đi tới 1 ví dụ nhỏ về GraphQL nhé! Một hệ thống thư viện lưu trữ 2 tables là Books và Authors, bài toán đặt ra là lấy ra tên sách với id = 123 và tên, tuổi tác giả của cuốn sách đó.

Thử tiếp cập bài toán với cách giải quyết thông thường là RESTful API nhé. Với REST, để giải quyết bài toán trên ta cần gọi 2 request, với request đầu tiên trả ra thông tin sách có id = 123, sau khi có được thông tin id tác giả ta lại tiếp tục gọi 1 request để lấy tiếp thông tin tác giả.

GET /books?id=123
GET /authors?id=456 (Giả sử id tác giả ở request trên trả ra 456)

Với GraphQL, công việc của bạn chỉ đơn giản là gọi 1 request, với data cần trả ra cụ thể là tên sách, tên tác giả và tuổi tác giả.

Một số đặc điểm của GraphQL:

  • GraphQL cho phép front-end chỉ định data trả về từ phía server.
  • Nhờ việc yêu cầu cụ thể data trả về(nothing more nothing less) GraphQL cho tốc độ nhanh, kể cả trên các thiết bị mobile có tốc độ kết nối internet chậm.
  • GraphQL API được tổ chức theo type system, không phải theo endpoint, vì vậy thông thường với GraphQL ta chỉ thấy 1 endpoint.
  • Không chia version trong quá trình phát triển, việc thêm hoặc bớt field chỉ cần thêm bớt trong type system mà không cần cập nhật version, việc này là ưu điểm nhưng cũng đôi khi là nhược điểm trong trường hợp các bạn muốn sử dụng lại version cũ của API.
  • Thích hợp với các hệ thống Microservice bởi trong hệ thống Microservice, data sẽ nằm rải rác ở các service khác nhau, nếu tiếp cận theo hướng REST, ta sẽ cần gọi request rất nhiều lần.

Một số ứng dụng lớn sử dụng GraphQL có thể kể tới như: Github, Facebook, Pinterest,…

2. GraphQL vs REST API

Vậy câu hỏi đặt ra là với những ưu điểm kể trên thì có phải chúng ta sẽ luôn dùng GraphQL trong mọi trường hợp? Liệu có còn chỗ đứng nào cho REST API?

Để trả lời câu hỏi trên chúng ta cùng xem xét sự khác nhau của GraphQL và REST API.

GraphQL giải quyết 2 vấn đề lớn mà REST API đang gặp phải là over-fetching và under-fetching. over-fetching nghĩa là với 1 lần gọi API, data trả về sẽ nhiều hơn với những gì client cần. Ngược lại, under-fetching nghĩa là sau một lần gọi API, data trả về sẽ không đủ với những gì client cần, do đó cần phải gọi thêm các API khác ở những lần sau.

(Over fetching và under fetching)

Bạn nhớ ví dụ mình nhắc đến ở đầu bài viết chứ, qua ví dụ đó ta có thể dễ dàng thấy được tình trạng under-fetching và over-fetching mà REST API gặp phải, bằng chứng là với lần gọi đầu tiên ta sẽ thiếu thông tin về tên và tuổi tác giả đồng thời data trả về đang dư thừa một số thông tin như thể loại sách, giá sách, giới tính tác giả.

So sánh GraphQL và REST API

REST API GraphQL
Dữ liệu trả về Gặp tình trạng over-fetching và under-fetching Sinh ra để giải quyết over-fetching và under-fetching
Độ phổ biến Ra đời vào những năm 2000, độ phổ biến lớn Ra đời vào năm 2012, độ phổ biến chưa lớn
Số lượng endpoints Nhiều endpoints Thường là 1 endpoint
Implement Đơn giản Phức tạp hơn
Upload file Đơn giản Phức tạp hơn
Caching Đơn giản Phức tạp hơn
Learning Curve Ít tốn công sức, bởi đây là những kiến thức cơ bản được giảng dạy trong môi trường học đường Tốn nhiều công sức hơn, hoặc phải trải qua quá trình training

Quay trở lại với câu hỏi được đặt ra ở đầu, có thể thấy bên cạnh những ưu điểm nổi bật của GraphQL thì vẫn còn tồn tại những nhược điểm mà tiêu biểu là việc implement phức tạp và triển khai sẽ tốn nhiều thời gian, công sức hơn với những team chưa tiếp xúc với GraphQL bao giờ. Do đó chúng ta cần sử dụng linh hoạt, tùy thuộc vào dự án và team để chọn công nghệ phù hợp.

3. Cách dùng GraphQL

Trong bài viết lần này mình sẽ dùng thư viện gqlgen của Golang để giới thiệu tới các bạn cách sử dụng GraphQL. Để bắt đầu sử dụng các bạn có thể làm theo hướng dẫn trên trang chủ của gqlgen hoặc clone từ repo này GitHub - ducthoitiki/graphql-example . Repo sau khi clone về sẽ có cấu trúc như sau:

Ở đây, có 2 file mà chúng ta cần đặc biệt quan tâm là schema.graphqls và schema.resolvers.go. schema.graphqls chứa định nghĩa về các type mà chúng ta sử dụng, trong ví dụ này là Todo và User. Đây cũng là nơi cho phép chúng ta khai báo mối quan hệ của các type. Trong ví dụ này ta có thể thấy được mối quan hệ của Todo và User, mỗi Todo sẽ thuộc về 1 User. Với type Query và Mutation, ta khai báo ra các operation mà chúng ta sẽ sử dụng:

  • Query cho phép lấy data nhưng không làm thay đổi data(chỉ xem).
  • Mutation thì ngược lại, làm thay đổi data(thêm, xóa, sửa).

Và việc đặt dấu ! ở cuối mỗi field sẽ ràng buộc field đó không được phép null.

type Todo {
id: ID!
text: String!
done: Boolean!
user: User!
}

type User {
id: ID!
name: String!
}

type Query {
todos: [Todo!]!
}

input NewTodo {
text: String!
userId: String!
}

type Mutation {
createTodo(input: NewTodo!): Todo!
}

Trong khi đó file schema.resolvers.go chứa các hàm chúng ta sẽ khai báo để xử lí request nhận được từ client. Trong trường hợp này là CreateTodo và Todos.

// CreateTodo is the resolver for the createTodo field.
func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
todo := &model.Todo{
Text: input.Text,
ID: fmt.Sprintf("T%d", rand.Int()),
User: &model.User{ID: input.UserID, Name: "user " + input.UserID},
}
r.todos = append(r.todos, todo)
return todo, nil
}

func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
return r.todos, nil
}

// Mutation returns generated.MutationResolver implementation.
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }

// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }

type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }

Để xem thành quả làm được trước hết ta chạy lệnh go run server.go để start server, tiếp tới truy cập vào localhost:8080 để tiến hành thử nghiệm query.

Lần lượt chạy 2 câu query dưới để thêm Todo và xem danh sách Todos hiện có nhé!

mutation createTodo {
  createTodo(input: { text: "todo", userId: "1" }) {
    user {
      id
    }
    text
    done
  }
}

query findTodos {
  todos {
    text
    done
    user {
       name
    }
  }
}

Sau khi có kiến thức về GraphQL, bạn hãy tự implement hàm xóa Todo theo id nhé. Với yêu cầu là đầu vào nhận vào id, đầu ra báo cho client thông báo xóa Todo thành công nếu id có tồn tại, hoặc thông báo Todo không tồn tại.

4. Tạm kết

“If all you have is a hammer, everything looks like a nail.” Việc lựa chọn công nghệ cũng cần tránh tư tưởng này, GraphQL không phải là một chiếc chìa khoá vạn năng có thể dùng trong mọi trường hợp. Qua bài viết, mình hi vọng các bạn có thể biết thêm một cách tiếp cận cho việc implement API nhờ đó mà có thêm sự lựa chọn trong quá trình lập trình. Cảm ơn các bạn đã dành thời gian đọc bài viết. Hẹn gặp lại các bạn trong những bài viết sau.

Tài liệu tham khảo:

Có thể bạn quan tâm:

4 Likes

Bài viết cực hữu ích với những người mới tìm hiểu về GraphQL như mình, tks bạn đã chia sẻ :ok_woman:

2 Likes

Caching khi dùng graphql và restapi sẽ khác nhau như thế nào ở phía client và server nhỉ, hóng bài tiếp theo quá @Duc_TOP_Thoi_Hai ơi :heart:

1 Like

Câu trả lời sẽ có ở phần sau nha anh :wink:

1 Like