GraphQL

0x01 概述

  • 官网链接:https://graphql.org/

  • GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。

    • GraphQL 对 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具
  • 特点:

    • 通过一个请求获取多个资源
    • 作为可以描述所有可能类型的系统,便于维护
  • 对比 RESTful API:

    项目 RESTful API GraphQL
    接口对应资源 一对一 一对多
    资源区分 通过 URL 通过类型

0x02 使用

(1)快速上手

以 Express 框架使用为例

  1. 参考《NodeJS 与 Express | 博客园-SRIGT》快速搭建 Express 环境

  2. 使用命令 npm install graphql express-graphql 安装 GraphQL 相关包

  3. 修改应用入口文件 app.js

    const express = require("express");
    const { graphqlHTTP } = require("express-graphql");
    const { buildSchema } = require("graphql");
    
    // 使用 GraphQL schema 语言构建 schema
    // 此处定义查询的语句和类型
    const schema = buildSchema(`
      type Query {
        hello: String
      }
    `);
    
    // 根 resolver
    // 此处定义查询对应的处理器
    const root = {
      hello: () => "Hello world!",
    };
    
    // 创建一个 GraphQL HTTP 服务器
    const app = express();
    
    // 使用 express-graphql 模块
    app.use(
      "/",
      graphqlHTTP({
        schema: schema,
        rootValue: root,
        graphiql: true,
      })
    );
    
    // 监听端口
    const PORT = 3000;
    
    // 启动服务器
    app.listen(PORT, () => console.log(`http://localhost:${PORT}`));
    
  4. 修改 package.json,添加 npm 脚本 "dev": "nodemon app.js"

  5. 使用命令 npm run dev 启动服务,并访问 http://localhost:3000/

  6. 在页面左侧输入框中输入以下语句:

    query {
      hello
    }
    
  7. 点击左上角 “执行” 或使用快捷键 Ctrl + Enter 执行查询,得到以下输出

    {
      "data": {
        "hello": "Hello world!"
      }
    }
    

(2)参数

a. 类型

  • GraphQL 参数类型分为基本类型复杂类型

  • 基本类型:

    • String:字符串
    • Int:整型
    • Float:浮点型
    • Boolean:布尔型
    • ID:唯一标识符
    • []:数组,如字符串数组 [String]
  • 复杂类型(自定义类型):在 buildSchema() 中通过 type 定义,语法类似 TypeScript

    • 举例:定义用户信息

      const schema = buildSchema(`
        type User {
          id: ID!
          name: String
          age: Int
        }
      `);
      

b. 传递

  • GraphQL 参数传递与 TypeScript 类似,通过 (): 定义形参和类型

    • 特别地,! 表示参数不能为空
  • 举例:分页获取用户信息

    let users = [];
    
    const schema = buildSchema(`
      type User {
        id: ID!
        name: String
        age: Int
      }
      type Query {
        getUsers(page: Int, limit: Int!): [User]
      }
    `);
    
    const root = {
      getUsers: (args) =>
        new Promise((resolve, _) => {
          users = [
            { id: 1, name: "Alex", age: 18 },
            { id: 2, name: "Bob", age: 20 },
            { id: 3, name: "Charlie", age: 25 },
          ];
    
          // 根据参数进行分页
          const page = args.page || 1;
          const limit = args.limit; // 该参数在 schema 中被设为不能为空的参数
    
          // 模拟数据库查询的延迟
          setTimeout(() => {
            resolve(users.slice((page - 1) * limit, page * limit));
          }, 1000);
        }),
    };
    

    并使用以下查询语句:

    query {
      getUsers(page: 2, limit: 2) {
        name, age
      }
    }
    

    获取到的结果为:

    {
      "data": {
        "getUsers": [
          {
            "name": "Charlie",
            "age": 25
          }
        ]
      }
    }
    

(3)客户端访问

  • GraphQL 支持在客户端使用特定语法访问接口

  • 举例:在客户端请求上述用户数据

    1. 在 Express 目录下新建 public 目录,其中新建 index.html

      <!DOCTYPE html>
      <html>
      <script>
        const page = 2;
        const limit = 2;
      
        const query = `query User($limit: Int!) { getUsers(page: ${page}, limit: $limit) { id name age } }`;
      
        fetch("http://localhost:3000/", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Accept: "application/json",
          },
          body: JSON.stringify({
            query, // 查询语句
            variables: { limit } // 查询参数
          }),
        })
          .then((res) => res.json())
          .then((data) => console.log(data));
      </script>
      
      </html>
      
      • 查询参数可以直接拼接在查询语句中,或通过 variables 在预先定义查询对象后传入
    2. 修改 app.js

      // ...
      const app = express();
      
      app.use(express.static("public"));
      // ...
      
    3. 访问 http://localhost:3000/index.html,并在控制台得到以下输出:

      {
        "data": {
          "getUsers": [
            {
              "id": "2",
              "name": "Bob",
              "age": 20
            }
          ]
        }
      }
      

(4)修改数据

  • GraphQL 中修改数据通过 Mutation 实现

    • 查询数据通过 Query 实现
  • 举例:添加用户信息

    let users = [];
    
    const schema = buildSchema(`
      type User {
        id: ID!
        name: String
        age: Int
      }
      type Query {
        getUsers(page: Int, limit: Int!): [User]
      }
      type Mutation {
        setUser(id: ID!, name: String, age: Int): User
      }
    `);
    
    const root = {
      // ...
      setUser: (args) => {
        if (users.find((user) => user.id === args.id)) {
          return users.find((user) => user.id === args.id);
        } else {
          args.id = (users.length + 1).toString();
          args.name = args.name || "New User";
          args.age = args.age || 18;
          users.push({ id: args.id, name: args.name, age: args.age });
          return { id: args.id, name: args.name, age: args.age };
        }
      },
    };
    

    并使用以下修改语句:

    mutation {
      setUser(id: 1, name: "David", age: 30) {
        id, name, age
      }
    }
    

    获取到的结果为:

    {
      "data": {
        "setUser": {
          "id": "1",
          "name": "David",
          "age": 30
        }
      }
    }
    

(5)构造类型

通过 GraphQLObjectType 构造类型

以上述用户信息为例

  1. 修改 app.js,使用构造方法重新定义用户类型

    const graphql = require("graphql");
    
    const UserType = new graphql.GraphQLObjectType({
      name: "User",
      fields: () => ({
        id: { type: graphql.GraphQLID },
        name: { type: graphql.GraphQLString },
        age: { type: graphql.GraphQLInt },
      }),
    });
    
  2. 重新定义查询类型(修改类型类似)

    let users = [];
    
    const QueryType = new graphql.GraphQLObjectType({
      name: "Query",
      fields: () => ({
        getUsers: {
          type: new graphql.GraphQLList(UserType), // 构造 UserType 数组类型
          args: {
            page: { type: graphql.GraphQLInt },
            limit: { type: graphql.GraphQLInt },
          },
          resolve: (_, args) =>
            new Promise((resolve, _) => {
              users = [
                { id: 1, name: "Alex", age: 18 },
                { id: 2, name: "Bob", age: 20 },
                { id: 3, name: "Charlie", age: 25 },
              ];
    
              const page = args.page || 1;
              const limit = args.limit;
    
              setTimeout(() => {
                resolve(users.slice((page - 1) * limit, page * limit));
              }, 1000);
            }),
        },
      }),
    });
    
    // 修改类型
    // const MutationType = new graphql.GraphQLObjectType({/* ... */});
    
  3. 重新定义 schema

    const schema = new graphql.GraphQLSchema({
      query: QueryType,
    });
    
  4. 移除根 resolver 和 graphqlHTTP()rootValue 参数

-End-

posted @ 2024-12-19 16:13  SRIGT  阅读(2)  评论(0编辑  收藏  举报