GraphQL 极速入门
前言
之前在自己的Blog项目 Blog++ 中使用了GraphQL,发现确实不错,所以写一篇关于GraphQL的入门文章。
What is GraphQL?
GraphQL是一种API查询语言,对API中的数据提供一套描述,使客户端能够准确地获得需要地数据。举个例子,在传统的RESTful API中,如果不对API做出声明,难以知道该API的用途,而且前端无法控制API返回的数据,而GraphQL就是对API的描述,并且前端能控制需要的数据,比如
// data
var data = {id: "abcxdesss",title: "JS is best language in the world",publishTime: "2018-12-12",userId: "sundial dreams",context: "JS is best language in the world",readNumber: 20000,starNumber: 2000,commentId: "commentId"
}
如果是RESTful API的话将会不多不少的返回所有字段,而如果使用GraphQL的话,就可以按需查询
query {queryArticle {title # 标题publishTime # 发布时间context # 内容}
}
将得到如下数据
{"queryArticle": {"title": "JS is best language in the world","publishTime": "2018-12-12","context": "JS is best language in the world"}
}
当然这只是举个简单的例子,以下是总结的GraphQL的优点
- 请求不多不少的数据
- 一个请求即可获得多个资源
- 类型系统
- 有专门的调试工具
- 维护API更容易
文章目录
本文将从以下几个方面来介绍GraphQL
GraphQL基本概念
Schema(服务端)
一个基本的Schema通过定义类型和类型字段来创建,比如
type User {name:String!age: Int!
}
其中String, Int是类型,!表示该字段不能为null。
- GraphQL内置的标量类型如下
- 枚举类型
用enum
关键字定义的类型
enum Color {GREENYELLOWREDBLUE
}
- 列表
用[]
表示
type MQ {id: IDqueue: [String!]!
}
注:[String]!表示整个列表不能为null,[String!]表示整个列表可以为null,但如果不为null,那么列表里就必须有值,[String!]!前两者的结合
- 接口
用interface
关键字来定义
interface Animal {name: String!
}
实现接口
type Dog implements Animal {name: String!classify: String
}
- 联合类型
使用union
定义的类型
union Result = AType | BType | CType
- 输入类型
使用input
定义的类型
input InputUser {name: String!age: Int!sex: Boolean!birthday: String!
}
使用输入对象类型
type Mutation {createUser(user: InputUser): Result
}
query & mutation查询(客户端)
使用查询来查询定义的Schema
- query
类似于http的GET,基本语法非常简单,结构如下
query QueryName {query1 {field1field2}
}
其中QueryName
表示查询名,即这次查询的名称,主要用于消除歧义,或者让该查询意思更加清晰,不过也可以省略
来个具体的例子
Schema定义
type Article {title: String!context: String!publishTime: String!readNumber: Int
}type Query {queryArticle: Article
}
query查询
query {queryArticle {titlecontext}
}
在http GET中可以使用?a=2&b=2
这种形式通过GET请求将数据传给后端,同样的GraphQL的query也能,即带参查询
Schema定义
type Query {queryArticle(title: String!): Article
}
query查询
query {queryArticle(title: "JS") {titlecontext}
}
除此之外,还提供了别名操作,比如将queryArticle重命名
query {jsArticle: queryArticle(title: "JS") {title context}
}
- mutation
类似于http的POST,语法结构与query类似
mutation {mutation1 {field1field2}
}
mutation主要是对后端数据的修改操作,就像POST一样,而query与GET一样只负责查询操作,除此之外与query还有一个主要区别:query是并行执行,而mutation是一个一个执行的
举个例子
Schema
type Error {msg: Stringerrno: Int!
}
type Mutation {createArticle(title: String!, context: String!): Error
}
mutation
mutation {createArticle(title: "python async/await", context: "python async/await") {errno}
}
- 片段
复用重复的字段,即将重复的查询字段抽离成一个片段来复用,片段的定义使用fragment
关键字,例如
注:必须与接口或联合类型结合使用
Schema
interface IAnimal {name: String!classify: String!
}
type Dog implements IAnimal{name: String!classify: String!moveSpeed: Int
}
type Bird implements IAnimal {name: String!classify: String!flySpeed: Int
}
type Query {queryAnimal: IAnimal
}
query
query Animal {canfly: queryAnimal(classify: "canfly") {...Animal}canrun: queryAnimal(classify: "canrun") {...Animal}
}
fragment Animal on IAnimal {nameclassify
}
- 内联片段
用来去实现接口类型的字段,以上面的schame为例
query {queryAnimal(classify: "canrun") {nameclassify... on Dog {moveSpeed}}
}
- 变量
允许动态的将参数传入到query和mutation查询中
query Query($data: Data) {query1(data: $data) {field1field2}
}
- 指令
对字段的一系列操作
query {query1(condition: Boolean!) {field1 @skip(if: condition)field2 @include(if: condition)}
}
其中@include(if: Boolean)
仅在参数为true时包含此字段,@skip(if: Boolean)
仅在参数为true时跳过此字段
GraphQL基本使用
基本使用
开始之前,我们需要安装graphql包
npm install --save graphql
开始一个最简单的例子
const graphql = require("graphql");
const { buildSchema } = graphql;
// 编写schema
const schemaString = `type Article {id: ID!title: String!context: StringpublishTime: String!readNumber: IntstarNumber: IntUserId: ID!commentId: ID! }# 指定根查询type Query {queryArticle: Article}# 指定查询schema {query: Query}
`;
const schema = buildSchema(schemaString);
// 定义query
const query = `query {queryArticle {titlecontextpublishTime}}
`;
const data = {id: "abcxdesss",title: "JS is best language in the world",publishTime: "2018-12-12",userId: "sundial dreams",context: "JS is best language in the world",readNumber: 20000,starNumber: 2000,commentId: "commentId"
};
const rootValue = {// 解析函数,需要与schema 的 queryArticle一致queryArticle () {// 模拟API调用// 或者直接 return datareturn new Promise(resolve => {setTimeout(() => resolve(data), 1000)});}
};
graphql.graphql(schema, query, rootValue).then(({ data: { queryArticle } }) => queryArticle).then(console.log);
当然也可以写一个graphQL文件,然后将graphQL Schema都写入到文件里
gql.graphql文件
type Article {id: ID!title: String!context: StringpublishTime: String!readNumber: IntstarNumber: IntUserId: ID!commentId: ID!
}
type Query {queryArticle: Article
}
schema {query: Query
}
main.js
function readFile (filename) {return fs.readFileSync(filename).toString();
}
const schema = buildSchema(readFile("./gql.graphql"));
除了使用GraphQL语法来构建Schema和query之外,还可以使用graphqljs来构建
比如
type User {id: ID!name: String!age: Intsex: Booleanhobby: [String!]
}
type Query {queryUser: [User!]
}
type Mutation {createUser(id: ID!, name: String!): User
}
schema {query: Querymutation: Mutation
}
使用graphqljs来构建就是
const {graphql,GraphQLID,GraphQLNonNull,GraphQLObjectType,GraphQLSchema,GraphQLList,GraphQLBoolean,GraphQLString,GraphQLInt} = require("graphql");const UserType = new GraphQLObjectType({name: "User", // type Userfields: {id: { type: new GraphQLNonNull(GraphQLID) },name: { type: new GraphQLNonNull(GraphQLString) },age: { type: GraphQLInt },sex: { type: GraphQLBoolean },hobby: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) }}
});
const QueryType = new GraphQLObjectType({name: "Query",fields: {queryUser: {type: new GraphQLList(new GraphQLNonNull(UserType)),resolve (_) { // 为该字段定义一个解析函数return [{ id: "user1", name: "sundial dreams", hobby: ["sleeping"], age: 21, sex: true },{ id: "user2", name: "daydreams", hobby: ["coding", "movie", "game"] },]}}}
});
const MutationType = new GraphQLObjectType({name: "Mutation",fields: {createUser: {type: UserType, // 即返回值类型args: { // 即函数参数id: { type: new GraphQLNonNull(GraphQLID) },name: { type: new GraphQLNonNull(GraphQLString) }},resolve (_, { id, name }) {// 为该字段定义一个解析函数console.log(id, name);return {id, name, age: 21, sex: true, hobby: ["code"]}}}}
});const schema = new GraphQLSchema({types: [UserType], // !!!注册类型query: QueryType,mutation: MutationType
});const query = `query {user: queryUser {idnamehobby}
}`;
const mutation = `mutation {createUser(id: "user3", name: "dpf") {idname}
}`;
graphql(schema, query).then(({ data: { user } }) => user).then(console.log);
使用上面这种方式构建schema的好处就是比较清晰,而且可以基于nodejs模块化机制,将schema模块化构建
更多实例
接口
接口Schema定义如下
interface IAnimal {name: String!classify: String!}type Dog implements IAnimal {name: String!classify: String!move: String}type Bird implements IAnimal {name: String!classify: String!fly: String}type Query {queryAnimal(classify: String!): IAnimal}schema {query: Query}
定义两个query查询如下
# 内联查询
query InlineQuery {queryAnimal(classify: "move") {name... on Dog {move}}
}# 片段 + 内联查询
fragment animalFragment on IAnimal {nameclassify
}query FragmentQuery {queryAnimal(classify: "fly") {...animalFragment... on Bird {fly}}
}
使用graphql包来构建类型,如下
const {graphql,GraphQLSchema,GraphQLString,GraphQLNonNull,GraphQLObjectType,GraphQLInterfaceType} = require("graphql");
// 公共字段
const animalFields = {name: { type: new GraphQLNonNull(GraphQLString) },classify: { type: new GraphQLNonNull(GraphQLString) }
};const AnimalInterface = new GraphQLInterfaceType({name: "IAnimal",fields: animalFields,resolveType (value) { // !!! 需要对接口类型进行运行期转换console.log(value); // value 就是 queryAnimal 传过来的// 根据 value返回不同的值if (value.hasOwnProperty("fly")) return BirdType;if (value.hasOwnProperty("move")) return DogType;return null;}}
);const DogType = new GraphQLObjectType({name: "Dog",fields: {...animalFields,move: { type: GraphQLString }},interfaces: [AnimalInterface] // 实现接口
});const BirdType = new GraphQLObjectType({name: "Bird",fields: {...animalFields,fly: { type: GraphQLString }},interfaces: [AnimalInterface] // 实现接口
});const QueryType = new GraphQLObjectType({name: "Query",fields: {queryAnimal: {type: AnimalInterface,args: {classify: { type: new GraphQLNonNull(GraphQLString) }},resolve (_, { classify }) {// 解析函数console.log(classify);if (classify === "fly") return {name: "bird",classify,fly: "i can fly"};else if (classify === "move") return {name: "dog",classify,move: "i can move"};}}}
});const schema = new GraphQLSchema({types: [AnimalInterface, DogType, BirdType],query: QueryType
});const query = `
# 内联查询
query InlineQuery {queryAnimal(classify: "move") {name... on Dog {move}}
}
`;
const query2 = `
# 片段 + 内联查询
fragment animalFragment on IAnimal {nameclassify
}query FragmentQuery {queryAnimal(classify: "fly") {...animalFragment... on Bird {fly}}
}
`;
graphql(schema, query).then(console.log);
联合类型
Schema定义如下
type A {a: String
}
type B {b: String
}
type C {c: String
}
union AllType = A | B | C
type Query {queryAll: [AllType]
}
schema {query: Query
}
定义一个query查询
query UnionQuery {queryAll {... on A {a}... on B {b}... on C {c}}
}
使用graphqljs来构建,如下
const {graphql,GraphQLSchema,GraphQLString,GraphQLNonNull,GraphQLObjectType,GraphQLInterfaceType,GraphQLUnionType,GraphQLList} = require("graphql");
const AType = new GraphQLObjectType({name: "A",fields: {a: { type: GraphQLString }}
});
const BType = new GraphQLObjectType({name: "B",fields: {b: { type: GraphQLString }}
});
const CType = new GraphQLObjectType({name: "C",fields: {c: { type: GraphQLString }}
});const AllTypeUnion = new GraphQLUnionType({name: "AllType",types: [AType, BType, CType], // 联合类型resolveType (value) {if (value.hasOwnProperty("a")) return AType;if (value.hasOwnProperty("b")) return BType;if (value.hasOwnProperty("c")) return CType;return null;}
});
const Query = new GraphQLObjectType({name: "Query",fields: {queryAll: {type: new GraphQLList(AllTypeUnion),resolve () {return [{ a: "hello world" },{ b: "main() {}" },{ c: "sundial dreams" }]}}}
});
const schema = new GraphQLSchema({types: [AType, BType, CType, AllTypeUnion],query: Query
});
const query = `
query UnionQuery {all: queryAll {... on A {a}... on B {b}... on C {c}}
}
`;
graphql(schema, query).then(({ data: { all } }) => all).then(console.log);
搭建一个GraphQL服务
会写Schema,那么搭建GraphQL服务就非常简单了,不过还得使用express-graphql
包
安装express
express-graphql
npm install --save express express-graphql
基本使用
// main.js
const express = require("express");
const graphqlHTTP = require("express-graphql");
const schema = require("./graphql");
const app = express();
app.use("/graphql", graphqlHTTP({schema,graphiql: true // 启用GraphQL调试器
}));
app.listen(8000, err => console.log(err || "listen in 8000"));
// graphql.js 复用之前的例子
const {graphql,GraphQLSchema,GraphQLString,GraphQLNonNull,GraphQLObjectType,GraphQLInterfaceType,GraphQLUnionType,GraphQLList} = require("graphql");
const animalFields = {name: { type: new GraphQLNonNull(GraphQLString) },classify: { type: new GraphQLNonNull(GraphQLString) }
};
const AnimalInterface = new GraphQLInterfaceType({name: "IAnimal",fields: animalFields,resolveType (value) { // !!! 需要对接口类型进行运行期转换console.log(value); // value 就是 queryAnimal 传过来的// 根据 value返回不同的值if (value.hasOwnProperty("fly")) return BirdType;if (value.hasOwnProperty("move")) return DogType;return null;}}
);const DogType = new GraphQLObjectType({name: "Dog",fields: {...animalFields,move: { type: GraphQLString }},interfaces: [AnimalInterface] // 实现接口
});const BirdType = new GraphQLObjectType({name: "Bird",fields: {...animalFields,fly: { type: GraphQLString }},interfaces: [AnimalInterface] // 实现接口
});const QueryType = new GraphQLObjectType({name: "Query",fields: {queryAnimal: {type: AnimalInterface,args: {classify: { type: new GraphQLNonNull(GraphQLString) }},resolve (_, { classify }) { // 解析函数console.log(classify);// 模拟异步操作return new Promise((resolve, reject) => {setTimeout(() => { // 延时一秒if (classify === "fly") resolve({name: "bird",classify,fly: "i can fly"});else if (classify === "move") resolve({name: "dog",classify,move: "i can move"});}, 1000);});});}}}
});module.exports = new GraphQLSchema({types: [AnimalInterface, DogType, BirdType],query: QueryType
});
可以在浏览器地址栏上输入 localhost:8000/graphql
打开调试器
调试器功能比较丰富,比如支持提示,美化查询,历史记录等等,读者可以自行体验
更多参考
graphql官网:https://graphql.cn/learn/
graphqljs:https://graphql.cn/graphql-js/
express-graphql:https://graphql.cn/graphql-js/running-an-express-graphql-server/
工具类
Apollo Client(React 的 GraphQL客户端, 同样支持Vue Angular等):https://www.apollographql.com/docs/react/
Relay(React 的GraphQL客户端): https://relay.dev/docs/en/introduction-to-relay
标签:
相关文章
-
无相关信息