Graphql 要点

你可以通过一个单一入口端点得到你所有的数据能力。GraphQL 使用类型来保证应用只请求可能的数据,还提供了清晰的辅助性错误信息。应用可以使用类型,而避免编写手动解析代码。

Schema 和类型

类型与字段

查询和变更

内联片段(Inline Fragments)

变量

$episode- 变量名 Episode – 变量类型 ”JEDI” -默认值

元字段(Meta fields) #

Python 要点

GraphQL React

import ApolloClient from 'apollo-boost';

const client = new ApolloClient({
  uri: 'https://48p1r2roz4.sse.codesandbox.io',
});

import { gql } from "apollo-boost";
// or you can use `import gql from 'graphql-tag';` instead

...

client
  .query({
    query: gql`
      {
        rates(currency: "USD") {
          currency
        }
      }
    `
  })
  .then(result => console.log(result));

Connect your client to React

用到ApolloProvider ,

!!! 如果用到react-router 一般会在包裹在 root route 组件外层

import { ApolloProvider } from '@apollo/react-hooks';

const App = () => (
  <ApolloProvider client={client}>
    <div>
      <h2>My first Apollo app ?</h2>
    </div>
  </ApolloProvider>
);

Request data

在 添加ApolloProvider 之后 就可以用useQuery hook 请求数据

import { useQuery } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';

function ExchangeRates() {
  const { loading, error, data } = useQuery(gql`
    {
      rates(currency: "USD") {
        currency
        rate
      }
    }
  `);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :(</p>;

  return data.rates.map(({ currency, rate }) => (
    <div key={currency}>
      <p>
        {currency}: {rate}
      </p>
    </div>
  ));
}

REST GraphQL

RestFul的缺陷
假设,此时我们有两个资源分别是Book和Author,这两个资源对应的ER图如下

POST /books
GET /books/{id}
POST /authors
GET /authors/{id}

我们有一个需求,需要查询id=1的图书信息!
那我们的请求地址是这样的
GET /books/1
返回结果是这样的
[cc][{
“id”: 1,
“bookname”: Harry Potter,
“price”: 56.00,
“author_id”: 2
}][/cc]
这时候前端MM拿到这个结果后,傻了眼!这里怎么能直接返回author_id呢,难道直接把author_id显示在界面上么?不可能啊,界面上要显示的是author_name才行!
OK,那么在这种情况下,有两种方式可以解决问题!
(1) 跟后端沟通,让他增加一个接口
VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
那可以让后端封装一个接口,后端帮你把数据拼装好,提供API如下
GET /bookVOs/1

这样直接返回的结果就是
[cc]
[{
“id”: 1,
“bookname”: Harry Potter,
“price”: 56.00,
“author_name”: J. K. Rowling
}][/cc]
OK,回到正题,这样做的缺点主要有两个
前后端强耦合在一起,前端界面发生变动,后端VO对象跟着一起变
如果BookVO对象在手机端、PC端、APP端的显示内容都不一样,你可能在项目中会有BookPcVO类、BookH5VO类、BookAppVO类,VO类大大膨胀!
(2) 自己做适配
这个也很简单,前端获得结果后,取出author_id: 2这条记录,然后再去调用地址

Graphql的出现
OK,首先你要声明一下,你的图结构,嗯,用的就是Graphql的语法啦,像下面这样
[cc]type Book {
id: Int
bookname: String
authors: [Author]
}
type Author {
id: Int
name: String
age: String
}
type Query {
getBook(id: Int): Book
}
type Mutation {
addComment(input: AddCommentInput): Comment
}
input AddCommentInput { … }
schema {
query: Query
}[/cc]
对上面的语句进行一下解释。这里一共声明了三个类Book、Author、Query!其实,Book和Author大家都可以猜都出来是指啥,需要注意的一点是authors: [Author]这个地方,用了一个[]的语法,这代表Author是一个数组!
唯一需要说明的是,Query是什么鬼?
Query在这儿表明了该类型是这张图的入口,也就是根节点!我们的查询必须从根节点里的属性开始!这里我为了便于说明,只列了一个属性,也就是getBook,该属性是入口,必须从入口开始查!

这里还有一个Resolver的概念,就是说,GragphQL解析到getBook方法的时候,方法体内容是啥?是在Resovler中定义的,我这里就不贴配置了,感兴趣的可以自己去官网阅读!

OK,接下来就是第二个问题,怎么查?
根据上面我说的,只能从根开始,根是getBook,那语句怎么写呢?如下所示

那你想加个字段,显示作者名字呢?直接像下面这么写

一些疑问
(1)java语言中,对Graphql的支持如何?
在java中,有个jar包为graphql-java-tools提供了对Java的支持。
另外,考虑到现在大多是springboot项目,有大神封装好了starter包,供你们的springboot项目使用!
只需在项目中引入
[cc lang=”xml”]

com.graphql-java
graphql-spring-boot-starter
4.0.0

com.graphql-java
graphql-java-tools
4.3.0
[/cc]

路由处理器(Route Handlers)vs. 解析器(Resolvers)

首先使用Express实现一个hello world:
app.get('/hello', function (req, res){
res.send('Hello World!')
})

可以看到一个REST API请求的的生命周期:

  1. 服务器收到请求并提取出HTTP方法名(比如这里就是GET方法)与URL路径
  2. API框架找到提前注册好的、请求路径与请求方法都匹配的代码
  3. 该段代码被执行,并得到相应结果
  4. API框架对结果进行序列化,添加上适当的状态码与响应头后,返回给客户端

 

GraphQL差不多也是这样工作的,我们来看下这个对应的hello world例子
const resolvers = {
Query: {
hello:() => {
return'Hello world!';
},
},
};

//我们看到,这里并没有针对某个URL路径提供函数,而是把Query类型中的hello字段映射到一个函数上了。 在GraphQL中这样的函数我们称之为解析器(Resolver)
然后我们就可以这样发起一个查询:
query {
hello
}
// 如下响应 { "hello": "Hello, world!" }

至此,总结一下服务器对一个GraphQL请求的执行过程:

  1. 服务器收到HTTP请求,取出其中的GraphQL查询
  2. 遍历查询语句,调用里面每个字段所对应的Resolver。在这个例子里,只有Query这个类型中的一个字段hello
  3. Resolver函数被执行并返回相应结果
  4. GraphQL框架把结果根据查询语句的要求进行组装