Web

GraphQL 学习笔记

Posted by Kerwen Blog on August 22, 2025

GraphQL

GraphQL是Facebook开发的一种数据查询语言,它于2015年公开发布。
GraphQL API的出现是为了解决传统RESTful API在某些场景下的一些限制和挑战。
下面是一些导致GraphQL API出现的主要原因:

  • 减少多次请求:传统的RESTful API一次请求只能返回一个资源,而GraphQL API一次可以返回多个资源,因此可以减少多次请求的次数。
  • 避免过度获取和欠获取:传统的RESTful API通常以预定义的数据结构返回数据,导致客户端要么获取了过多的数据,要么获取了不足的数据。GraphQL API通过让客户端指定所需的数据结构,避免了这种问题,客户端可以精确地获取所需的字段和关联数据,避免了数据的浪费和不足。

由于restful api预定义好了每个接口的返回的数据,所以即使我只想要用户的name,却还是会返回用户的整个字段内容给我。而graphql查询,如果我只要用户的name,那么就只会返回name给我,不会返回整个用户字段给到用户,避免了数据的过度获取。

graphql是一种数据查询语言,GraphQL的API通常是一个URL对应多种查询,不同的查询是通过请求体来区分的。

Hello World

运行 GraphQL API 服务器的最简单方法是使用 Express

  1. 初始化一个project npm init -y
  2. 安装依赖

    1
    
     npm install express graphql-http graphql --save
    
  3. 搭建一个express server

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
     var express = require('express');
    
     var app = express();
     app.use('/', (req, res) => {
         res.send('Hello World');
     })
     app.listen(3001, ()=>{
         console.log('Server running on port 3001');
     })
    
  4. 添加graphQL

    1
    2
    
     var { buildSchema } = require('graphql');
     const { createHandler } = require('graphql-http/lib/use/express');
    

在 GraphQL 中,schema 定义了客户端可以查询的数据类型以及它们之间的关系。使用 buildSchema 函数可以创建 schema 对象。这个 schema 对象可以被传递给 GraphQL 的执行器,以便它可以执行来自客户端的查询,并返回所需的数据。

1
2
3
4
5
6
7
    // Construct a schema, using Graphql schema language. 1
    // The parameter of buildSchema function is a String type. 2
    var schema = buildSchema(`
        type Query {
            hello : String!
        }
    `);

这段schema 定义了所有可能的类型和操作,并且充当客户端和服务器之间的契约。注意,buildSchema方法的参数这里使用的是反引号. 在这个 schema 中,我们定义了一个类型:Query
Query类型是 GraphQL schema 中的预定义类型。其中定义了一些查询操作(方法),客户端可以使用这些操作从服务器中检索数据。
在Query类型中,我们定义了一个字段:hello。hello字段是一个字符串类型,它的值为String!,表示这是一个非空的字符串类型。
在实际应用中,我们需要提供一个名为hello的函数,用于处理查询操作。

1
2
3
4
5
6
    //The root provide a resolver function for each API endpoint 1
    var root = {
        hello: () => {
            return 'hello world';
        }
    };

在 GraphQL 中,root 对象是一个用于处理客户端请求的实际对象(根对象),它包含了所有的处理函数。hello 方法是箭头函数的表示方式,返回一个字符串 ‘hello world’,这个方法用于处理 hello 查询操作。在GraphQL 中,查询操作是客户端用于从服务器检索数据的一种方式。

1
2
3
4
5
6
7
8
    // Create and use the GraphQL handler.
    app.all(
        '/graphql',
        createHandler({
            schema: schema,
            rootValue: root,
        }),
    );
  1. 使用以下命令运行此 GraphQL 服务器:

    1
    
     node index.js
    

此时,你将有一个正在运行的 GraphQL API

  1. 创建一个客户端index.html

  2. 自己写一个客户端测试graphql太麻烦了,我们需要一个现成的 GraphQL 客户端来向 API 发出 GraphQL 查询。GraphiQL 是 GraphQL 的 IDE, 查询和探索 GraphQL API 的好方法。官方提供的方法是使用 ruru 包,该包捆绑了预构建的 GraphiQL 和一些流行的增强功能。 为此,请安装模块

    1
    
     npm install --save ruru
    

然后将以下内容添加到文件中,然后重新启动命令:node index.js

1
2
3
4
5
6
7
    const { ruruHTML } = require('ruru/server');

    // Serve the GraphiQL IDE.
    app.get('/', (_req, res) => {
        res.type('html');
        res.end(ruruHTML({ endpoint: '/graphql' }));
    });

导航到 http://localhost:3001,应该会看到一个允许您输入查询的界面。 在左侧输入

1
2
3
    {
        hello
    }

点击Execute Query, 可以看到右侧返回

1
2
3
4
5
    {
        "data": {
            "hello": "hello world"
        }
    }

数据类型

  • 基本数据类型: String, Int, Float, Boolean, ID(ID类型的本质是字符串类型,但是如果是ID类型就表示数据不能重复,但是GraphQL本身并没有内置的机制来强制确保ID类型的数据不重复,需要协同数据库共同控制),这几个类型都可以在schema声明的时候直接使用。
  • [类型]代表数组,例如[Int]代表整数数组。

默认情况下,每个类型都是可为 null 的 - 返回任何类型都是合法的。使用感叹号表示类型不能为空,不可为空的字符串string!

自定义数据类型

GraphQL中除了几种基本的数据类型,有两种预定义的数据类型,一种是Query,另一种是Mutation。 如果用户想自定义其它数据类型,则可以在buildSchema方法中通过type定义其它类型,例如我们想定义一个User类型和Post(博客)类型。

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
var schema = buildSchema(`
    type User {
        id: ID!
        name: String!
        posts: [ID!]
    }
    type Post {
        id: ID!
        title: String!
        content: String!
        author: ID!
    }
    type Query {
        hello : String!
        getUser: User!
        getPost: Post!
    }
`);


var root = {
    hello: () => {
        return 'hello world';
    },
    getUser() {
        return {
            id: '1',
            name: 'John Doe',
            posts: ['1', '2']
        };
    },
    getPost() {
        return {
            id: '1',
            title: 'Post Title',
            content: 'Post Content',
            author: '1'
        };
    }
};

在getUser和getPost方法的具体实现中,都需要返回对应的自定义数据类型,我们的返回值是字典类型,可以返回比期望的数据类型更多的键值对,因为graphQL只会匹配和期望的数据类型中相同的字段。但是不能更少(不会报错),但是这样在查询的时候对应被遗漏的字段的值就为null了,如果恰好被遗漏的字段的值不能为null,又恰好去查询了这个字段的值,那么查询就会报错。
由于getUser方法和getPost方法的返回类型都是自定义类型,在调用的时候我们必须使用{}来指定我们希望服务器端返回给我们哪几个字段的值

1
2
3
4
5
6
7
8
9
10
{
    getUser{
        id
        name
    }
    getPost{
        id
        title
    }
}

返回数据

1
2
3
4
5
6
7
8
9
10
11
12
{
    "data": {
        "getUser": {
        "id": "1",
        "name": "John Doe"
        },
        "getPost": {
        "id": "1",
        "title": "Post Title"
        }
    }
}

参数传递

和js传递参数一样,在小括号内定义行参,但是参数需要在schema中定义类型。
graphQL请求参数,参数的值如果是String,必须使用双引号,使用单引号会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Query {
    getUserById(id:ID!): User!
    getPostById(id: ID!): Post!
}

getUserById({id}){
    var User1={id:'001',name:'Alice',posts:['11','12']}
    var User2={id:'002',name:'Mary',posts:['13','14']}
    if(User1.id === id){
        return User1
    } else {
        return User2;
    }
},
getPostById({id}){
    var Post1={id:'1',title:'What is API',content:'API is an interface ..', author:'1'}
    var Post2={id:'1',title:'What is GraphQL',content:'GraphQL is an langu...', author:'1'}
    if(Post1.id === id){
        return Post1
    } else {
        return Post2;
    }
}

发出以下请求

1
2
3
4
5
6
7
{
    getUserById(id:"001"){
        id
        name
        posts
    }
}

得到的结果

1
2
3
4
5
6
7
8
9
10
11
12
{
    "data": {
        "getUserById": {
            "id": "001",
            "name": "Alice",
            "posts": [
                "11",
                "12"
            ]
        }
    }
}

query和mutation

在GraphQL中,Query和Mutation是两种特殊的根操作类型,都是预定义好的,用于定义可执行的查询和变更操作。 Query类型: Query类型用于定义可以执行的读取操作。它表示你可以从服务器获取数据的能力。在前面我们一直是查询操作,所以我们所有查询操作的定义其实都写在Query type下面。

1
2
3
4
type Query {
    getUser(id: ID!): User!
    getPost(id: ID!): Post!
}

Mutation类型: Mutation类型用于定义可以执行的写入或修改操作。它表示你可以向服务器发送请求以更改数据的能力。通常,Mutation`类型中的字段对应于可以对服务器上的数据进行修改的操作。 假设目前我们需要创建一条Account数据。我们可以和之前一样使用type自定义Account数据类型:

1
2
3
4
5
6
type Account {
    id: ID!
    name: String
    age: Int
    salary(city: String): Int
}

但是使用type定义出来的数据类型只能作为查询类型,却不能作为请求的输入类型。所以我们还需要使用input定义输入类型AccountInput作为请求的输入参数:

1
2
3
4
5
input AccountInput {
    name: String
    age: Int
    city: String
}

接下来我们定义mutation类型以及其createAccount操作,createAccount的输入参数是AccountInput类型:

1
2
3
type Mutation {
    createAccount(input: AccountInput): Account
}

注意,由于如果只有Mutation类型而没有Query类型的话,graphQL不支持,所以我们再添加一个Query类型:

1
2
3
type Query {
    getAccount(id: ID!): Account
}

完整代码:

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
var schema = buildSchema(`
    type Account {
        id: ID!
        name: String
        age: Int
        salary(city: String): Int
    }
    input AccountInput {
        name: String
        age: Int
        city: String
    }
    type Query {
        getAccount(id: ID!): Account
    }
    type Mutation {
        createAccount(input: AccountInput): Account
    }
`);

var root = {
    createAccount({ input }) {
        const name = input.name;
        const age = input.age;
        // salary is a field resolver, not a direct value
        return {
            id: "001",
            name,
            age,
            salary: ({ city }) => (city === 'Shenzhen' ? 2000 : 1000)
        };
    },
    getAccount({ id }) {
        let name = '';
        let age = null;
        if (id === '001') {
            name = 'Tom';
            age = 29;
        }
        return {
            id,
            name,
            age,
            salary: ({ city }) => (city === 'Shenzhen' ? 2000 : 1000)
        };
    }
};

输入以下查询

1
2
3
4
5
6
7
8
mutation{
    createAccount(input:{name:"xx1", age:18, city:"Shenzhen"}){
        id
        name
        age
        salary(city:"Shenzhen")
    }
}

得到的结果

1
2
3
4
5
6
7
8
9
10
{
    "data": {
        "createAccount": {
        "id": "001",
        "name": "xx1",
        "age": 18,
        "salary": 2000
        }
    }
} # Apollo ApolloServerPluginDrainHttpServer apolloServerEventHandlerPlugin apolloServerLandingPagePluging # GraphQL In TypeScript type-graphql

raphql-ws

GraphQL + Angular

CodeGen

使用GraphQL和Apollo构建Angular ToDo应用

GraphQL真香入门教程
自动生成GraphQL接口文件的步骤
GraphQL 的 正确打开方式 (apollo-client前戏)
apollo-client 和 apollo-server的正确打开方式
Apollo_Angular_GraphQl_Start Angular Integration with GraphQL: Complete Guide to Setup and Usage
Complete guide to GraphQL in Angular