Graphql学习指南
欢迎来到Graphql的世界
Graphql设计原则
- 分层: 查询字段层次分明
- 强类型
Graphql查询语言
边和连接
Graphql内置标量类型
- Int
- Float
- String
- Boolean
- ID
片段
query {
Lift(id: "jazz-cat") {
name
status
capacity
night
trailAccess {
name
diffculty
}
}
}
这是一个正常的查询, 但有很多时候, 我们想把Lift常用信息都提取出来
例如name
status
capacity
night
这四个字段在很多query里都有, 那么我们应该如何减少重复编写这几个字段呢, 使用fragment
可以解决我们的问题
上面的query可以改造为
fragment liftInfo on Lift {
name
status
capacity
night
}
query {
Lift(id: "jazz-cat") {
...liftInfo
trailAccess {
name
diffculty
}
}
}
联合类型
当在一个列表中, 需要返回不同类型的对象, 如何编写query呢
query schedule {
agenda {
...on Workout {
name
reps
}
...on StudyGroup {
name
subject
students
}
}
}
返回就是这样的
{
data: {
agenda: [
{ name: "xxx", "subject": "aaaa", students: 12},
{ name: "aaa": reps: "xxxx"}
]
}
}
也可以用片段去查联合类型
fragment workout on Workout {
name
reps
}
fragment study on StudyGroup {
name
subject
students
}
接口
当一个字段可能返回多种对象时, 那么就需要使用接口
定义
interface Agenda {
name
}
type Workout implements Agenda{
name,
reps
}
查询
query schedule {
agenda {
name
...on Workout {
reps
}
}
}
变更
mutation createSong {
addSong(title: 'aaa', numberOne: true) {
id
title
numberOne
}
}
设置schema
标量类型
graphql自身只有5个内置标量类型
如何自定义标量类型
scalar DateTime
type Photo {
id: ID!
created: DateTime!
}
https://github.com/stylesuxx/graphql-custom-types
枚举
表明变量只能是指定的几种值
enum PhotoCategory {
SELFIE
ACTION
GRAPHIC
}
type Photo {
id: ID!
category: PhotoCategory!
}
连接和列表
列表声明 | 定义 |
---|---|
[Int] | 可空的整数值列表 |
[Int!]不可空的整数值列表 | |
[Int]! | 可空的整数值非空列表 |
[Int!]! | 不可空的整数值非空列表 |
大多数情况都是不可空的非空列表
[Int!]和[Int]!的区别
- [Int!]允许[], 但不允许[null]
- [Int]!允许[null], 但不允许[]
一对一连接
一张照片只会被一个用户拍出来
type User {
name: String
}
type Photo {
url: String
postedBy: User!
}
一对多连接
type User {
postedPhotos: [Photo!]!
}
找出这个用户的所有照片
多对多连接
假设我们的人工智能能识别出每张照片的用户是谁
那么一张照片就可能会有多个用户, 而一个用户也会出现在多张图片里
type User {
...
inPhotos: [Photo!]
}
type Photo {
...
taggedUsers: [User!]
}
直通类型
之前一对一, 一对多, 多对多, 一般都是资源与资源之间的关系
但有种情况需要查 关系不是呢是的信息
例如查找人某个用户群里, 这些人认识了多久, 在哪里认识的
type Friendship {
friends: [User!]!
howLong: Int!
whereWeMet: Location
}
不同类型的列表
有时需要在同一个字段描述不同类型的对象
可以用到union 或 interface
union
一般两个完全没有不同类型的对象, 用这个来做整合
union AgendaItem = StudyGroup | Workout
type StudyGroup {
name: String!
subject: String!
}
type Workout {
name: String!
reps: Int!
}
type Query {
agenda: [AgendaItem!]!
}
interface
而一般是从集成自同意父类的话, 建议用这个interface
子类一定要包含接口里的所有字段说明
interface AgendaItem = {
name: String!
}
type StudyGroup implements AgendaItem{
name: String!
subject: String!
}
type Workout implements AgendaItem{
name: String!
reps: Int!
}
type Query {
agenda: [AgendaItem!]!
}
参数
type Query {
User(id: ID!) User!
}
query {
User(id: "xxxx") {
name
avater
}
}
变更
schema
type Mutation {
postPhoto(
name: String!
description: String
category: PhotoCategory=PORTRAIT
)
}
graphql
mutation {
postPhoto(name: "xxxx") {
id
url
posteBy {
name
}
}
}
输入类型
schema
input PostPhotoInput {
name: String!
description: String
category: PhotoCategory=PORTRAIT
}
type Mutation {
postPhoto(input: PostPhotoInput!): Photo!
}
graphql
mutation newPhoto($input, PostPhotoInput!) {
postPhoto(input: $input) {
id
url
posteBy {
name
}
}
}
input 还有一个重要功能, 通用化某些查询条件
schema
input DateRange {
start: DateTime!
end: DateTime!
}
input DataSort {
sort: SortDirection = DESCENDING
sortBy: SortAblePhotoField = created
}
type User {
postedPhotos(paging: DatePage, sorting: DataSort): [Photo!]!
inPhotos(paging: DatePage, sorting: DataSort): [Photo!]!
}
type Query {
allUsers(paging: DatePage, sorting: DataSort): [User!]!
allPhotos(paging: DatePage, sorting: DataSort): [Photo!]!
}
订阅类型
schema
- newPhoto: 把新照片推送给所有订阅newPhoto的客户端
- newUser: 把有用户创建时, 把user推送给所有订阅newUser事件的端
type Subscription {
newPhoto(category: PhotoCategory): Photo!
newUser: User!
}
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
graphql
subscription {
newPhoto(category: "ACTION") {
id
name
url
}
}
Schema文档
- 3个双引号表明多行注释
- 2个双引号代表单行注释
"""
A user who has been authorized by Github at least once
"""
type User {
"User name "
name
}
创建一个GraphQL api
这个文章比较详细的说 如何用apollo+mongoDB 创建一个用户发布图片的服务
还不错
Graphql 客户端
如果是js的话, graphql-request
用来发送graphql请求比较简便些
例如会把参数转换为符合graphql的形式
Query组件
这里其实像把api请求语句抽离到外部写, 而组件需要数据的话, 直接通过Query组件获取
import React from 'react'
import Users from './Users'
import { gql } from 'apollo-boost'
export const ROOT_QUERY = gql`
query allUsers {
totalUsers
allUsers {
githubLogin
name
avatar
}
}
`
React组件使用
import React from 'react'
import { Query } from 'react-apollo'
import { ROOT_QUERY } form './App'
const Users = () => {
<Query query={ROOT_QUERY}>
{({ data, loading, refetch }) => loading ?
<p>loading users...</p> :
<UserList
count={data.totalUsers}
users={data.allUsers}
refetchUsers={fretch}
/>
}
}
Mutation组件
与Query组件相类似的还有Mutation组件, 用法基本是一致的
主要多了一个refetchQueries表明, 发送变更后重新获取哪些查询
Mutation gql编写
import { Query, Mutation } from 'react-apollo'
import { gql } from 'apollo-boost'
export const ADD_FAKE_USERS_MUTATION = gql`
mutation addFakeUsers($count:Int!) {
addFakeUsers(count: $count){
githubLogin
name
avatar
}
}
`
使用示例
const { ADD_FAKE_USERS_MUTATION } from './mutationGql'
<Mutation
mutation={ADD_FAKE_USERS_MUTATION}
VARIABLES={{COUNT: 1}}
>
{
addFakeUsers =>
<button onClick={addFakeUsers}>Add Fake Users<button>
}
</Mutation>
添加缓存
取回策略
apollo默认的缓存策略是cache-first
策略名 | 处理方式 |
---|---|
cache-first | 优先取本地缓存,没有本地缓存则发起网络 |
cache-only | 只从缓存中取, 缓存没有则报错 |
cache-and-network | 先从缓存取, 不管缓存有没有, 在有网络的情况下都发送一个请求来更新缓存 |
network-only | 每次都发送请求, 但会缓存数据 |
no-cache | 始终发送请求, 并不缓存数据 |
持久缓存
npm install apollo-cache-persist
这个包主要作用是将缓存存储到本地缓存
import ApolloClient, { InmemoryCache } from 'apollo-boost'
import { persistCache } from 'apollo-cache-persist'
const cache = new InmemoryCache()
persistCache({
cache,
storage: localStorage
})
const client = new ApolloClient({
cache,
...
})
现实世界中的GraphQL
订阅
主要使用 graphql-subscriptions, graphql-transports-ws 这两个库
schema优先开发
graphql 可以自定义设置mock数据
笔者总结
用来作为入门书籍还不错, 也有一些实践操作, 4分吧(总分5)