初识
什么是?
特点
- 请求你所要的数据不多不少
- 获取多个资源只用一个请求
- 描述所有可能的类型系统、
使用场景?
1.我们需要什么数据需要什么格式,后端就按照什么格式给我们返回什么样的数据结构,我们要哪些字段,后端就只给我们返回我们需要的这些字段, 其他的都不返回,前端就和后端解耦了---》 在前端直接写查询, 后端只管给前端返回前端查询的这些数据;
2.使用GraphQL前端自己写查询,这个页面需要哪些需哪数据,后端就返回给哪些数据, 这是考虑到后端所有的接口都在同一个域下面
比较复杂的系统,后端都会分为不同的域, 用户域,商品域,基础模块域,交易域等等,这时即使用了GraphQL也可能需要请求不同的接口
解决:
- 多写一个GraphQL
- 自己写一个node中间层,中间层来处理这些接口数据的聚合,换句话说,中间层来聚合成一个GraphQL查询来返回给前端, 中间层分别取调用服务端的三个接口,然后把三个接口返回的数据聚合成前端所需要的
操作类型
query
mutation
subscription
查询
按字段&&想要的数据结构查询
query HeroInfos{ //query关键字 + 操作名
hero {
name
# 查询可以有备注!
friends {
name
}
}
}
}
变更 mutation
任何导致写入的操作都应该显式通过变更(mutation)来发送。
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
查询与变更的区别:
查询字段时,是并行执行。变更字段时,是线性执行,一个接着一个。
变量定义
变量前缀必须为 $
所有声明的变量都必须是标量、枚举型或者输入对象类型
变量定义可以是可选的或者必要的 $name! 必选 $name 可选
参数
//参数写在查询字符串内
query HumanInfo{
{
human(id: "1000") { //参数
name
height
}
}
}
{
human(id: "1000") { //参数
name
height(unit: FOOT) //参数
}
}
GraphQL 拥有一级方法将动态值提取到查询之外,然后作为分离的字典传进去。这些动态值称为变量。
使用变量之前需要:
- 使用
$variableName
替代查询中的静态值。 - 声明
$variableName
为查询接受的变量之一。 - 将
variableName: value
通过传输专用(通常是 JSON)的分离的变量字典中。
# { "graphiql": true, "variables": { "episode": JEDI } }
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
//带默认值
query HeroNameAndFriends($episode: Episode=‘JEDC’) {
hero(episode: $episode) {
name
friends {
name
}
}
}
alias
通过不同参数来查询相同字段 示例中,两个 hero
字段将会存在冲突,可以取一个别名,就可以在一次请求中得到两个结果
{
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
片段fragment
可复用单元。组织一段字段在需要的地方引入
fragment comparisonFields on Character {
name
appearsIn
friends {
name
}
}
{
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
片段内使用变量
query HeroComparison($first: Int = 3) {
leftComparison: hero(episode: EMPIRE) {
...comparisonFields
}
rightComparison: hero(episode: JEDI) {
...comparisonFields
}
}
fragment comparisonFields on Character {
name
friendsConnection(first: $first) {
totalCount
edges {
node {
name
}
}
}
}
内联片段??
查询的字段返回的是接口或者联合类型,可能需要使用内联片段来取出下层具体类型的数据
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
... on Human {
height
}
}
}
元字段(Meta fields)
不知道你将从 GraphQL 服务获得什么类型时,需要一些方法在客户端来决定如何处理这些数据。GraphQL 允许在查询的任何位置请求 __typename,
以获得那个位置的对象类型名称。
{
search(text: "an") {
__typename
... on Human {
name
}
... on Droid {
name
}
... on Starship {
name
}
}
}
directive 指令
作用:动态改变查询结构
@include(if: Boolean)
仅在参数为 true
时,包含此字段。
@skip(if: Boolean)
如果参数为 true
,跳过此字段。
{
"episode": "JEDI",
"withFriends": true
}
query Hero($episode: Episode, $withFriends: Boolean!) {
hero(episode: $episode) {
name
friends @include(if: $withFriends) {
name
}
}
}
schema和类型
对象类型和字段
type Character {
name: String! //非空string字段
appearsIn: [Episode!]! // 非空数组 每一项是episode对象
}
参数
---所有参数必须具名传递
type Starship {
id: ID!
name: String!
//unit定义默认值 unit参数没传递,默认METER
length(unit: LengthUnit = METER): Float
}
标量类型
默认标量类型:
Int
:有符号 32 位整数。Float
:有符号双精度浮点值。String
:UTF‐8 字符序列。Boolean
:true
或者false
。ID
:表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人为可读型。
自定义标量:
例如,定义一个 Date
类型:scalar Date
然后就取决于如何定义将其序列化、反序列化和验证。例如,你可以指定 Date
类型应该总是被序列化成整型时间戳,而客户端应该知道去要求任何 date 字段都是这个格式。
枚举类型
- 验证这个类型的任何参数是可选值的的某一个
- 一个字段总是一个有限值集合的其中一个值
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
无论在哪处使用了 Episode
,都可以肯定它返回的是 NEWHOPE
、EMPIRE
和 JEDI
之一。
interface 接口
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
union 联合类型
联合类型和接口十分相似,但是它并不指定类型之间的任何共同字段。
union SearchResult = Human | Droid | Starship
输入类型
输入对象看上去和常规对象一模一样,除了关键字是 input
而不是 type
:
input ReviewInput {
stars: Int!
commentary: String
}
验证
通过使用类型系统,可以预判一个查询是否有效。服务器和客户端可以在无效查询创建时就有效地通知开发者,而不用依赖运行时检查。
片段不能引用其自身或者创造回环,因为这会导致结果无边界。
{
hero {
...NameAndAppearances
friends {
...NameAndAppearances
friends {
...NameAndAppearances
}
}
}
}
fragment NameAndAppearances on Character {
name
appearsIn
}
...
根字段&&解析器
Query: {
human(obj, args, context, info) {
return context.db.loadHumanByID(args.id).then(
userData => new Human(userData)
)
}
}
解析器函数接收 4 个参数:
obj
上一级对象,如果字段属于根节点查询类型通常不会被使用。args
可以提供在 GraphQL 查询中传入的参数。context
会被提供给所有解析器,并且持有重要的上下文信息比如当前登入的用户或者数据库访问对象。info
一个保存与当前查询相关的字段特定信息以及 schema 详细信息的值
context
提供了一个数据库访问对象,用来通过查询中传递的参数 id
来查询数据,因为从数据库拉取数据的过程是一个异步操作,该方法返回了一个 Promise 对象。只有解析器能感知到 Promise 的进度,GraphQL 查询只关注一个包含着 name
属性的 human
字段是否返回,在执行期间如果异步操作没有完成,则 GraphQL 会一直等待下去,因此在这个环节需要关注异步处理上的优化。