GraphQL
0x01 概述
-
官网链接:https://graphql.org/
-
GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。
- GraphQL 对 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具
-
特点:
- 通过一个请求获取多个资源
- 作为可以描述所有可能类型的系统,便于维护
-
对比 RESTful API:
项目 RESTful API GraphQL 接口对应资源 一对一 一对多 资源区分 通过 URL 通过类型
0x02 使用
(1)快速上手
以 Express 框架使用为例
-
参考《NodeJS 与 Express | 博客园-SRIGT》快速搭建 Express 环境
-
使用命令
npm install graphql express-graphql
安装 GraphQL 相关包 -
修改应用入口文件 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}`));
-
修改 package.json,添加 npm 脚本
"dev": "nodemon app.js"
-
使用命令
npm run dev
启动服务,并访问 http://localhost:3000/ -
在页面左侧输入框中输入以下语句:
query { hello }
-
点击左上角 “执行” 或使用快捷键 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 支持在客户端使用特定语法访问接口
-
举例:在客户端请求上述用户数据
-
在 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
在预先定义查询对象后传入
- 查询参数可以直接拼接在查询语句中,或通过
-
修改 app.js
// ... const app = express(); app.use(express.static("public")); // ...
-
访问 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
构造类型
以上述用户信息为例
-
修改 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 }, }), });
-
重新定义查询类型(修改类型类似)
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({/* ... */});
-
重新定义 schema
const schema = new graphql.GraphQLSchema({ query: QueryType, });
-
移除根 resolver 和
graphqlHTTP()
中rootValue
参数
-End-