文档
- Schema定义规范
- 层级定义规范
- Apollo最佳实践
Apollo GraphQL
-
对一些产品链复杂场景中应用,工作剖析机制
-
架构体系
-
GraphQL 操作类型
-
Object Type & Scalar Type
-
模式 Schema
-
解析函数Resolver
-
请求格式
-
环境部署
Apollo GraphQL ==工作机制==
-
复杂场景中 工作机制
- API字段的定制化,按需取字段
- API的聚合,一次请求拿到所有的数据
- 后端不再需要维护接口的版本号
- 完备的类型校验机制,
GraphQL-体系结构
GraphQL是描述GraphQL服务器行为的规范。它是关于如何处理请求和响应(如支持的协议,服务器可以接受的数据格式,服务器返回的响应格式等)的一组准则。客户端对GraphQL的请求服务器称为查询。 GraphQL的另一个重要概念是其传输层不可知论性。它可以与任何可用的网络协议(例如TCP,websocket或任何其他传输层协议)一起使用。它对数据库也是中立的,因此您可以将其与关系数据库或NoSQL数据库一起使用。
- 可以使用下面列出的三种方法中的任何一种来部署GraphQL Server-
1. 具有连接数据库的GraphQL服务器
该体系结构具有带集成数据库的GraphQL Server,通常可以与新项目一起使用。收到查询后,服务器读取请求有效负载并从数据库中获取数据。这称为解决查询。返回给客户端的响应遵循官方GraphQL规范中指定的格式
在上图中,GraphQL服务器和数据库集成在单个节点上。客户端(桌面/移动设备)通过HTTP与GraphQL服务器通信。服务器处理该请求,从数据库中获取数据并将其返回给客户端
2. GraphQL Server集成现有系统
在上图中,GraphQL API充当客户端和现有系统之间的接口。客户端应用程序与GraphQL服务器通信,该服务器依次解析查询。
3. 混合方式
最后,我们可以结合以上两种方法来构建GraphQL服务器。在这种体系结构中,GraphQL服务器将解析收到的任何请求。它将从连接的数据库或集成的API检索数据。下图所示-
操作类型
GraphQL 的操作类型可以是 query、mutation或subscription,描述客户端希望进行什么样的操作
- query 查询:获取数据,比如查找,CRUD 中的 R
- mutation 变更:对数据进行变更,比如增加、删除、修改,CRUD 中的 CUD
- substription 订阅:当数据发生更改,进行消息推送
Object Type & Scalar Type
如果一个 GraphQL 服务接受到了一个 query,那么这个 query 将从 Root Query 开始查找,找到对象类型(Object Type)时则使用它的解析函数 Resolver 来获取内容,如果返回的是对象类型则继续使用解析函数获取内容,如果返回的是标量类型(Scalar Type)则结束获取,直到找到最后一个标量类型。
- 对象类型:用户在 schema 中定义的 type,用于描述层级或者树形数据结构。对于树形数据结构来说,叶子字段的类型都是标量数据类型。几乎所有 GraphQL 类型都是对象类型。Object 类型有一个 name 字段,以及一个很重要的 fields 字段。fields 字段可以描述出一个完整的数据结构。例如一个表示地址数据结构的 GraphQL 对象为:
const AddressType = new GraphQLObjectType({
name: 'Address',
fields: {
street: { type: GraphQLString },
number: { type: GraphQLInt },
formatted: {
type: GraphQLString,
resolve(obj) {
return obj.number + ' ' + obj.street
}
}
}
});
- 标量类型:GraphQL 中内置有一些标量类型 String、Int、Float、Boolean、ID
Schema 定义
-
灵活而增强的Schema ,schema就是协议,规范,或者可以当他是接口文档,GraphQL规定,每一个schema有一个根(root)query和根(root)mutation。
- 一次查询只实现一个单一目标。
- 尽量避免使用片段,指令之类的高级特性。
- 使用POST提交数据
- 参数与查询语句分离
- 所有 GraphQL schema 内的类型都必须要有唯一的名字
- 所有 schema 内定义的类型和指令都不能以"__"(双下划线)开头命名,因为这是 GraphQL 内省系统专用。
- 任何 GraphQL Schema 的最基本单元都是类型,GraphQL 中有 8种 类型(标量 对象 接口 联合 枚举型 输入对象 列表型 非空型)
- 类型定义和返回值的类型必须要一致
- GraphQL是强类型的。也就是说,我们在定义Schema时,类似于使用SQL,显式地为每一个域定义类型的,比如:
schema { #定义查询
query: UserQuery
}
type UserQuery { #定义查询的类型
user(id:ID) : User #指定对象以及参数类型
}
type User { #定义对象
id:ID! # !表示该属性是非空项
name:String
age:Int
}
- 定义一个根查询
type Query {
# 可以查询的字段和参数
shares(start: Int = 0, limit: Int = 10, creatorId: ID): [Share!]!
share(shareId: ID!): Share!
commentInfo(shareId: ID!, start: Int = 0, limit: Int = 10): CommentInfo!
}
- 查询请求的模型可以用下面的图来表示
Resolvers
Resolver是一组函数,可为GraphQL查询生成响应。简单来说,解析器充当GraphQL查询处理程序。GraphQL架构中的每个解析器函数都接受四个位置参数,如下所示
fieldName: (parent, args, context, info) => data;
| ARGUMENT | DESCRIPTION |
|---|---|
| parent | 包含从父字段上的解析程序返回的结果的对象 |
| args | 参数传递给查询中的字段的对象 |
| context | 特定查询中所有解析器共享的对象 |
| info | 它包含有关查询执行状态的信息,包括字段名称,从根目录到字段的路径 |
- 解析器返回结果格式
| NO. | 参数描述 | |
|---|---|---|
| 1 | null或undefined(这表示无法找到该对象) | |
| 2 | array | |
| 3 | promise | |
| 4 | scalar | object |
请求格式
GraphQL 最常见的是通过 HTTP 来发送请求,如何通过 Get/Post 方式来执行下面的 GraphQL 查询呢:
query {
me {
name
}
}
Get 是将请求内容放在 URL 中,Post 是在 content-type: application/json 情况下,将 JSON 格式的内容放在请求体里
# Get 方式
http://myapi/graphql?query={me{name}}
# Post 方式的请求体
{
"query": "...",
"operationName": "...",
"variables": { "myVariable": "someValue", ... }
}
返回的格式一般也是 JSON 体
# 正确返回
{
"data": { ... }
}
# 执行时发生错误
{
"errors": [ ... ]
}
GraphQL-环境部署
- 搭建服务端
- 采用 apollo-server-express 快速搭建服务端,创建一个package.json文件,其中将包含GraphQL服务器应用程序的所有依赖项。
{
"name": "apollo-server",
"private": true,
"scripts": {
"start": "nodemon --ignore data/ server.js"
},
"dependencies": {
"apollo-server-express": "^1.4.0",
"body-parser": "^1.18.3",
"cors": "^2.8.4",
"express": "^4.16.3",
"graphql": "^0.13.2",
"graphql-tools": "^3.1.1"
},
"devDependencies": {
"nodemon": "1.17.1"
}
}
##安装依赖
yarn install || npm install
- 定义schema 和 resolvers
const server = new ApolloServer({
typeDefs,
resolvers
});
创建对应的schema.graphql
type Query {
test: String
}
创建解析器文件 resolvers.js
const Query = {
test: () => 'Test Success, GraphQL server is up & running !!'
}
module.exports = {Query}
- 创建server.js并配置Graphql
const bodyParser = require('body-parser');
const cors = require('cors');
const express = require('express');
const db = require('./db');
const port = process.env.PORT || 9000;
const app = express();
const fs = require('fs')
const typeDefs = fs.readFileSync('./schema.graphql',{encoding:'utf-8'})
const resolvers = require('./resolvers')
const {makeExecutableSchema} = require('graphql-tools')
const schema = makeExecutableSchema({typeDefs, resolvers})
app.use(cors(), bodyParser.json());
const {graphiqlExpress,graphqlExpress} = require('apollo-server-express')
app.use('/graphql',graphqlExpress({schema}))
app.use('/graphiql',graphiqlExpress({endpointURL:'/graphql'}))
app.listen(
port, () => console.info(
`Server started on port ${port}`
)
);
const express = require("express");
const { ApolloServer } = require("apollo-server-express");
const typeDefs = require("./schema");
const resolvers = require("./resolvers");
const PORT = 4000;
const app = express();
const server = new ApolloServer({
typeDefs,
resolvers,
playground: {
endpoint: `/graphql`,
settings: {
"editor.theme": "light"
}
}
});
server.applyMiddleware({ app });
app.listen(PORT, () =>
console.log(
`🚀 Server ready at http://localhost:${PORT}${server.graphqlPath}`
)
);
- 搭建客户端
- 项目使用vue技术栈,基于vue-apollo来编写
yarn add vue-apollo graphql apollo-boost
import Vue from "vue";
import ApolloClient from "apollo-boost";
import VueApollo from "vue-apollo";
Vue.use(VueApollo);
const apolloClient = new ApolloClient({
// 你需要在这里使用绝对路径
uri: "http://localhost:4000/graphql"
});
const apolloProvider = new VueApollo({
defaultClient: apolloClient
});
new Vue({
el: "#app",
apolloProvider,
render: h => h(App)
});
- 配置支持 .gql || .graphql 文件后缀的 webpack loader
// vue.config.js
module.exports = {
// 支持 gql 文件
chainWebpack: config => {
config.module
.rule("graphql")
.test(/\.(graphql|gql)$/)
.use("graphql-tag/loader")
.loader("graphql-tag/loader")
.end();
}
};
一个查询获取多种结果集
query shareDetailPage($shareId: Int!, $creatorId:ID!, $start: Int!, $limit: Int = 10) {
# 分享详情
shareDetail: share (shareId: $shareId) {
shareId: id
title
desc
where
logoUrl
attchments
}
# 评论信息
commentInfo(shareId: $shareId, start: $start, limit: $limit) {
totalCount
comments {
id
userId
content
commentTime
}
}
# TA的分享
hisShares (creatorId: $creatorId) {
shares {
title
desc
where
startTime
}
}
}
fragment
- 片段 fragment:是 GraphQL 组合拼装的基本单元,它通用选择集字段的重用得以实现,减少了文档中的重复文本。
- 🌰 例如,我们想要获取某个用户的朋友以及和他互为朋友的人的共通信息:
query noFragments {
user(id: 4) {
friends(first: 10) {
id
name
profilePic(size: 50)
}
mutualFriends(first: 10) {
id
name
profilePic(size: 50)
}
}
}
// 这些重复的字段可以提取进一个 fragment(片段)中,
// 然后被父级 fragment(片段)或者 query(查询)组合:
query withFragments {
user(id: 4) {
friends(first: 10) {
...friendFields
}
mutualFriends(first: 10) {
...friendFields
}
}
}
fragment friendFields on User {
id
name
profilePic(size: 50)
}