/

使用Node.js和Express创建GraphQL服务器

使用Node.js和Express创建GraphQL服务器

這是一個關於如何使用Node.js和Express創建GraphQL服務器的簡單教程。

首先,創建一個新的Node.js項目,如果您還沒有設置一個的話:

1
npm init --y

這個命令會創建我們使用npm所需的package.json文件。

安裝npm包expressgraphqlexpress-graphql

1
npm install express graphql express-graphql

創建一個app.js文件,並開始初始化Express服務器:

1
2
3
4
5
6
7
const express = require('express')

const app = express()

app.listen(3000, () => {
console.log('App listening on port 3000')
})

現在我們添加express-graphql庫,它是一個中間件(middleware),我們將它應用於/graphql路由:

1
2
3
4
5
6
7
8
9
10
const express = require('express')
const graphqlHTTP = require('express-graphql')

const app = express()

app.use('/graphql', graphqlHTTP())

app.listen(3000, () => {
console.log('App listening on port 3000')
})

我們需要傳遞一個對象,該對象包含了 schema 屬性,該屬性必須包含一個schema定義。

我們首先需要定義一個schema!

創建一個schema.js文件,然後在其中引入graphql庫,使用對象解構語法,我們獲取GraphQLSchemaGraphQLObjectTypeGraphQLString這幾個對象:

1
2
const graphql = require('graphql')
const { GraphQLSchema, GraphQLObjectType, GraphQLString } = graphql

然後,我們通過初始化一個新的GraphQLSchema實例,並傳遞一個對象給它,該對象包含一個query屬性。該屬性是一個GraphQLObjectType對象的實例:

1
2
3
4
5
6
7
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
//...
}),
})

module.exports = schema

在這個新對象中,我們必須指定一個name和一個fields屬性。這個fields屬性是一個對象,其中包含我們的schema的每個字段的屬性。在這個例子中,我們設置了一個hello字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
hello: {
type: GraphQLString,
resolve() {
return 'world'
},
},
},
}),
})

resolve()方法返回字符串world,這意味著當我們請求hello字段時,我們將得到這個字符串。

以下是完整的schema.js文件內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const graphql = require('graphql')
const { GraphQLSchema, GraphQLObjectType, GraphQLString } = graphql

const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
hello: {
type: GraphQLString,
resolve() {
return 'world'
},
},
},
}),
})

module.exports = schema

現在,讓我們回到我們的app.js文件。

這是我們有的:

1
2
3
4
5
6
7
8
9
10
const express = require('express')
const graphqlHTTP = require('express-graphql')

const app = express()

app.use('/graphql', graphqlHTTP())

app.listen(3000, () => {
console.log('App listening on port 3000')
})

現在我們需要引入schema.js文件:

1
const schema = require('./schema.js')

並將其添加到我們傳遞給graphqlHTTP()構造函數的對象中:

1
2
3
4
5
6
app.use(
'/graphql',
graphqlHTTP({
schema: schema,
})
)

好的!

我們現在可以測試一下,看看它是否正常工作。我們可以使用GraphiQL,這是一個很好的測試GraphQL API的工具。

它已經安裝好了,要啟用它,我們需要給graphqlHTTP構造函數傳遞另一個屬性:

1
2
3
4
5
6
7
app.use(
'/graphql',
graphqlHTTP({
schema: schema,
graphiql: true,
})
)

現在你運行node app.js,在瀏覽器中訪問http://localhost:3000/graphqlURL,你將看到GraphiQL的頁面:

你可以測試第一個API調用,傳遞這個查詢:

1
2
3
{
hello
}

這是返回結果:

現在讓我們來構建一個更複雜的schema。

一個具有嵌套類型的schema。

我腦中有一個例子是一個博客文章。

一篇博客文章有標題、描述,還有作者。作者有一個名字。

讓我們來想一下。

首先,我們添加一組文章和作者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const posts = [
{
title: 'First post',
description: 'Content of the first post',
author: 'Flavio',
},
{
title: 'Second post',
description: 'Content of the second post',
author: 'Roger',
},
]

const authors = {
Flavio: {
name: 'Flavio',
age: 36,
},
Roger: {
name: 'Roger',
age: 7,
},
}

這就是我們將從哪獲取數據。

接下來,我們定義了3個GraphQLObjectType實例:

  • authorType,定義了作者的數據
  • postType,定義了文章的數據
  • queryType,主要一個

我們先從作者開始。一個作者有名字和年齡。

我們使用了GraphQLInt類型,我們需要先引入它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLInt } = graphql

//...

const authorType = new GraphQLObjectType({
name: 'Author',
fields: {
name: {
type: GraphQLString,
},
age: {
type: GraphQLInt,
},
},
})

接下來是postType。一篇文章有標題和描述(都是字符串),還有一個作者。一個作者是authorType類型,我們剛剛定義了這個類型,並且它有一個解析器。

我們從source參數中獲取作者的名字,source參數是傳遞給文章對象的參數,然後根據它查找作者數據。我們將其返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const postType = new GraphQLObjectType({
name: 'Post',
fields: {
title: {
type: GraphQLString,
},
description: {
type: GraphQLString,
},
author: {
type: authorType,
resolve: (source, params) => {
return authors[source.author]
},
},
},
})

注意,解析器函數可以是異步的,所以你可以使用async/await從數據庫或者網絡中查詢資源。

接下來是queryType,這是我們將添加到schema中的根類型。在其中,我們定義了2個字段:

  • post 一篇博客文章,根據id來識別
  • posts 文章列表

它們都有一個解析器函數來查找在posts數組中的數據:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const queryType = new GraphQLObjectType({
name: 'Query',
fields: {
post: {
type: postType,
args: {
id: { type: GraphQLInt },
},
resolve: (source, { id }) => {
return posts[id]
},
},
posts: {
type: new GraphQLList(postType),
resolve: () => {
return posts
},
},
},
})

注意到了新的GraphQLList類型,我們使用它來將postType包裹起來,表示它是一個postType對象的列表。我們必須在頂部引入它:

1
2
3
4
5
6
7
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLList,
GraphQLInt,
} = graphql

這樣就完成了。我們需要將它添加到我們的schema中,我們就完成了:

1
2
3
const schema = new GraphQLSchema({
query: queryType,
})

這是完整的代碼:

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
const graphql = require('graphql')
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLList,
GraphQLInt,
} = graphql

const posts = [
{
title: 'First post',
description: 'Content of the first post',
author: 'Flavio',
},
{
title: 'Second post',
description: 'Content of the second post',
author: 'Roger',
},
]

const authors = {
Flavio: {
name: 'Flavio',
age: 36,
},
Roger: {
name: 'Roger',
age: 7,
},
}

const authorType = new GraphQLObjectType({
name: 'Author',
fields: {
name: {
type: GraphQLString,
},
age: {
type: GraphQLInt,
},
},
})

const postType = new GraphQLObjectType({
name: 'Post',
fields: {
title: {
type: GraphQLString,
},
description: {
type: GraphQLString,
},
author: {
type: authorType,
resolve: (source, params) => {
return authors[source.author]
},
},
},
})

const queryType = new GraphQLObjectType({
name: 'Query',
fields: {
post: {
type: postType,
args: {
id: { type: GraphQLInt },
},
resolve: (source, { id }) => {
return posts[id]
},
},
posts: {
type: new GraphQLList(postType),
resolve: () => {
return posts
},
},
},
})

const schema = new GraphQLSchema({
query: queryType,
})

module.exports = schema

在Glitch上查看完整代碼.

tags: [“GraphQL”, “Node.js”, “Express”, “JavaScript”]