PJCHENder 未整理筆記

[GraphQL] GraphQL 起步走

2020-04-09

[GraphQL] GraphQL 起步走

keywords: graphql, Udemy

GraphQL with React: The Complete Developers Guide @ Udemy by Stephen

Prerequisite

1
2
3
4
5
# server
$ npm install nodemon json-server

# express and graphql
$ npm install express express-graphql graphql

Queries

Simple without relations

Schema

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const UserType = new GraphQLObjectType({
name: 'User',
fields: {
id: { type: GraphQLString },
firstName: { type: GraphQLString },
age: { type: GraphQLInt },
},
});

const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
user: {
type: UserType,

// 接收參數
args: {
id: {
type: GraphQLString,
},
},

// 回傳資料
resolve(parentValue, args) {
return axios
.get(`${BASE_URL}/users/${args.id}`)
.then((response) => response.data); // { user: '23', firstName: 'Bill' }
},
},
},
});

Queries

1
2
3
4
5
6
7
{
user(id: "41") {
id
firstName
age
}
}

With nested queries

User has one Company:

Schema

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const CompanyType = new GraphQLObjectType({
name: 'Company',
fields: {
id: { type: GraphQLString },
name: { type: GraphQLString },
description: { type: GraphQLString },
},
});

const UserType = new GraphQLObjectType({
name: 'User',
fields: {
id: { type: GraphQLString },
firstName: { type: GraphQLString },
age: { type: GraphQLInt },
company: {
type: CompanyType,
resolve(parentValue) {
return axios
.get(`${BASE_URL}/companies/${parentValue.companyId}`)
.then((response) => response.data);
},
},
},
});

const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
user: {
type: UserType,
args: {
id: {
type: GraphQLString,
},
},
resolve(parentValue, args) {
return axios
.get(`${BASE_URL}/users/${args.id}`)
.then((response) => response.data);
},
},
// company
company: {
type: CompanyType,
args: {
id: {
type: GraphQLString,
},
},
resolve(parentValue, args) {
return axios
.get(`${BASE_URL}/companies/${args.id}`)
.then((response) => response.data);
},
},
},
});

Query

1
2
3
4
5
6
7
8
9
10
11
{
user(id: "41") {
name
company {
name
}
}
company(id: "2") {
name
}
}

With bidirectional query

  • User has one Company.
  • Company belongs to many Users.

Schema

resolve circular references of “type” with function in fields property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const CompanyType = new GraphQLObjectType({
name: 'Company',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
description: { type: GraphQLString },
users: {
type: new GraphQLList(UserType),
resolve(parentValue) {
return axios
.get(`${BASE_URL}/companies/${parentValue.id}/users`)
.then((response) => response.data);
},
},
}),
});

const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: { type: GraphQLString },
firstName: { type: GraphQLString },
age: { type: GraphQLInt },
company: {
type: CompanyType,
resolve(parentValue) {
return axios
.get(`${BASE_URL}/companies/${parentValue.companyId}`)
.then((response) => response.data);
},
},
}),
});

Query

1
2
3
4
5
6
7
8
{
company(id: "2") {
name
users {
firstName
}
}
}

Mutations

Create a record

Schema

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
addUser: {
// 最後要回傳的資料格式
type: UserType,

// 可以帶入的參數
args: {
firstName: {
type: new GraphQLNonNull(GraphQLString),
},
age: {
type: new GraphQLNonNull(GraphQLInt),
},
companyId: {
type: GraphQLString,
},
},

// 要做的事,以及可以得到的回傳值
resolve(parentValue, args) {
const { firstName, age, companyId } = args;
return axios
.post(`${BASE_URL}/users`, {
firstName,
age,
companyId,
})
.then((response) => response.data);
},
},
},
});

module.exports = new GraphQLSchema({
mutation,
});

Mutation

1
2
3
4
5
6
7
mutation {
addUser(firstName: "Aaron", age: 26) {
id
firstName
age
}
}

Concepts

Express 會透過 middleware 來處理 GraphQL 進來的請求:

Imgur

許多情況下,會有一個專門用來處理 GraphQL 請求的 server,在這個 server 中定義和 GraphQL 有關的架構,而實際上撈取資料的過程,則是在透過 API 和其他 database server 請求資料:

Imgur

Queries and Mutations

Queries and Mutations @ GraphQL

為操作命名(operation name)

為各種操作命名,可以讓除錯的時候更方便,也可以在 server-side 紀錄(log)的時候更清楚:

operation name @ GraphQL

1
2
3
4
5
query fooBar {
foo(id: "3") {
name
}
}

將回傳的資料建立別名(aliases)

aliases @ GraphQL

把原本回傳屬性為 user 的名稱改名 billalex

1
2
3
4
5
6
7
8
9
10
{
bill: user(id: "23") {
firstName
age
}
alex: user(id: "40") {
firstName
age
}
}

Fragments

當 query 中「欄位內容」重複時,可以使用 Fragments。

舉例來說,當 billalex 都是要撈取 User Type 中的相同欄位時(i.e., firstName, age, company),可以將這些欄位建立成一個 fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 建立名為 userDetail 的 fragment
{
bill: user(id: "23") {
...userDetail
}
alex: user(id: "40") {
...userDetail
}
}

fragment userDetail on User {
firstName
age
company {
name
}
}

參考

掃描二維條碼,分享此文章