Xây dựng Forum với React và GraphQL (P2)
📅January 22, 2019
Chào các bạn! Tiếp tục với series xây dựng forum bằng GraphQL, React, Apollo và Prisma.
Các bạn có thể xem phần trước ở đây. Ở phần này, mình sẽ setup project ở tầng backend (Graphql) dựa trên một template có sẵn của graphql-boilerplates (Một template được build dựa trên graphql-yoga, prisma, prisma-binding. Đúng với stack mà chúng ta đang theo đuổi)
Lưu ý là mình chỉ xây dựng dựa trên cấu trúc của template trên. Do đó, toàn bộ quá trình xây dựng sẽ được build from scratch :D
Tạo mới project
mkdir forum-graphql
cd forum-graphql
yarn init -y
Nếu không muốn sử dụng yarn thì các bạn cũng có thể sử dụng npm
như bình thường.
Sau khi chạy yarn init -y
thì một file có tên là package.json
sẽ được sinh ra trong folder forum-graphql
, file này là file cấu hình của toàn bộ nodejs app. Nó chứa tất cả các dependencies, scripts và một số options khác cần thiết cho app.
Tạo Graphql Server
Tiếp theo là khởi tạo file index.js.
Tại forum-graphql/
mkdir src
touch src/index.js
Install graphql-yoga
yarn add graphql-yoga
Chúng ta setup server như sau:
Tại forum-graphql/src/index.js
const { GraphQLServer } = require('graphql-yoga')
const typeDefs = `
type Query {
info: String!
}
`
const resolvers = {
Query: {
info: () => `This is the API of a Forum GraphQL`,
},
}
const server = new GraphQLServer({
typeDefs,
resolvers,
})
server.start(() => console.log(`Server is running on http://localhost:4000`))
typeDefs
để khởi tạo GraphQL Schema. Ở đây mình khởi tạo 1 schema có type làQuery
và field làinfo
có kiểu dữ liệu làString!
. Dấu chấm than ở cuối có nghĩa là field này không chấp nhận giá trịnull
.resolvers
Object này để implementation cho thằng Schema được định nghĩa ở trên. Hình dung là mọi hoạt động, logic sẽ được viết trong object này.server
Object server chính của app.
Testing Server
Khởi động server bằng cách run command:
Tại forum-graphql/
node src/index.js
Trên màn hình console sẽ hiển thị http://localhost:4000
. Để test API, mở browser và truy cập vào đường URL trên.
Sau khi truy cập vào URL http://localhost:4000
, hiển thị trước mắt các bạn là giao diện của GraphQL Playground (IDE này chuyên để test GraphQL API, về mục đích sử dụng thì nó giống như postman của RESTful API)
Click vào Button Schema bên góc bên phải, lúc này mọi thông tin của GraphQL Schema sẽ được hiển thị. Mình rất thích IDE này, bởi vì không chỉ đơn thuần để test API, IDE này giống như 1 documentation thực sự, giúp cho lập trình viên dễ dàng hơn trong việc viết Query.
OK, giờ hãy thử viết câu query đầu tiên:
query {
info
}
Execute bằng cách click Play button ở giữa hoặc CMD+ENTER
Done, chúc mừng bạn đã thực hiện thành công câu GraphQL Query đầu tiên.
Như mình đã giải thích ở phần trên. Sau khi định nghĩa Schema ở typeDefs
, chúng ta implement nó bằng cách viết các hàm trong resolvers
. Đối với resolvers
, chúng ta có thể control hoặc tuỳ biến giá trị mà chúng ta muốn trả về, miễn sao phù hợp với kiểu dữ liệu của field mà chúng ta return. Ví dụ Schema trên, field info
chỉ chấp nhận dữ liệu trả về là String!
, dấu !
có nghĩa là giá trị đó không thể null
.
Vậy thì điều gì sẽ xảy ra nếu chúng ta return về null
?
Tại forum-graphql/src/index.js
, edit lại như sau:
const resolvers = {
Query: {
info: () => null,
},
}
Sau đó run lại câu query trên (Lưu ý là chúng ta phải khởi động lại server trước khi send Query nhé, CTRL+C => node src/index.js
)
Như các bạn có thể thấy, mọi giá trị trả đều phải tuân thủ và xoay quanh Schema. Điều này giảm thiểu tối đa những sai lầm từ lập trình viên.
Bản chất của GraphQL Schema
Cốt lõi của GraphQL API là GraphQL Schema
Thật vậy, mọi logic và luồng hoạt động của GraphQL API đều dựa trên những định nghĩa của GraphQL Schema. Mình sẽ nói chi tiết về GraphQL Schema ở phần này để mọi người hiểu rõ hơn, phục vụ cho những phần tiếp theo của series.
Mỗi GraphQL Schema bao gồm 3 root types: Query, Mutation và Subscription. 3 loại root types này tương ứng với 3 toán tử trong GraphQL: queries, mutations and subscriptions.
Để dễ hình dung thì mình xin đưa ra một ví dụ như sau:
type Query {
users: [User!]!
user(id: ID!): User
}
type Mutation {
createUser(name: String!): User!
}
type User {
id: ID!
name: String!
}
Trong trường hợp này, chúng ta có 3 root fields: users
, user
và createUser
. Tất cả các field trong Query
, Mutation
và Subscription
đều là root field. Type User
ở dưới cùng chỉ để định nghĩa cho [User!]!
trong type Query
mà thôi.
users: [User!]!
field này trả về danh sách bao gồm các objectUser
. Kiểu dữ liệu[User!]!
(cách viết này của scalar type) có nghĩa là array bao gồm các objectUser
và không được phép null.
Ví dụ, dữ liệu trả về có dạng[null, { name: 'Alex' }, null]
,[null]
sẽ vi phạm điều kiện của schema. Điều này ngăn chặn việc return về những array null hoặc array bao gồm những object null.user(id: ID!): User
field này trả về single objectUser
hoặcnull
và nhận vào 1 parameterid
với typeID!
(required). Trả về một user vớiid
là parameter vừa truyền vào. Dữ kiểu trả về làUser
object.createUser(name: String!): User!
field này là function operation dùng để tạo mới user. Nhận vào 1 parameter làname
và luôn luôn trả vềUser
object vừa tạo.
Dưới đây là Query
và Mutation
cho Schema vừa khởi tạo ở trên:
# Query for all users
query {
users {
id
name
}
}
# Query a single user by their id
query {
user(id: "user-1") {
id
name
}
}
# Create a new user
mutation {
createUser(name: "Thuan") {
id
name
}
}
Mỗi 1 câu GraphQL Query truy vấn ở trên đều return về id
và các field được định nghĩa trong Schema. Chúng ta cũng có thể bỏ return id
hoặc các field khác nhưng phải đảm bảo rằng có ít nhất 1 field được trả về.
Lời kết
Ở phần sau, mình sẽ đi vào Query và Mutation một cách chi tiết hơn.
Cảm ơn mọi người đã đọc bài!