# 前言

本篇文章可能稍微长了一点,您需要先准备好时间,以免中途要去上厕所什么的,发生中断。

我们一般都是用REST API, 即后端定义好API的数据结构和参数,前端再传参请求获取数据。

比如我们有个获取用户列表的接口:GET /user/list, 返回用户id,用户名,创建时间:

[
    {
      "id": 1,
      "name": "jack",
      "created_at": "2021-05-10 13:14:15"
    },
    {
      "id": 2,
      "name": "tom",
      "created_at": "2021-05-07 12:14:05"
    }
]

如果业务需求发生了变化,比如接口要返回用户头像时,就需要后端去改代码,增加用户头像的字段:

[
    {
      "id": 1,
      "name": "jack",
      "created_at": "2021-05-10 13:14:15",
      "avatar": "https://www.avatar.com/01.jpg"
    },
    {
      "id": 2,
      "name": "tom",
      "created_at": "2021-05-07 12:14:05",
      "avatar": "https://www.avatar.com/02.jpg"
    }
]

当后面又要添加字段时,又需要后端去改接口的代码。

有时候后端沉迷在思考中,可能不会很快响应,前端就要等着,或者去模拟些假数据来搞,这样不好。

于是有人就说能不能提供一种接口:让前端来指定字段,让前端来做连表查询,让前端来做分页查询呢?答案是当然有的,比如今天介绍的graphql就是其中的一种方式。

# 关于graphql

grapql的相关资料可以看:https://graphql.cn/learn/

它提供了一套完整的查询语句,只要后端支持graphql,就能立马使用。一定程度上减少了后端的工作,给前端提供了更好的用户体验。

注意,不是说有了grapql,后端就不用写查询代码,可以开心的去摸鱼了。不是的奥,后端不用写REST API的代码了,但要写graphql的语句哦。

不过,有一说一,有的场景下它还是非常适合的,比如你是做saas的,要开放些查询api给客户,用它呢就可以减少很多工作。

我们来一起试试吧

# 服务端

我们来搞个服务端,这里就node js来做例子,基于koa框架的apollo-server-koa。 先安装下:

npm i apollo-server-koa
npm i graphql

写个app.js,启动它吧:

const Koa = require('koa');
const { ApolloServer, gql } = require('apollo-server-koa');

async function startApolloServer() {
    const typeDefs = gql`
    type Query {
      hello: String,
      hot: String
    }
  `;

    // Provide resolver functions for your schema fields
    const resolvers = {
        Query: {
            hello: () => 'Hello world!',
            hot: () => 'Very hot!'
        },
    };

    const server = new ApolloServer({ typeDefs, resolvers });
    await server.start();

    const app = new Koa();
    server.applyMiddleware({ app });

    await new Promise(resolve => app.listen({ port: 4000 }, resolve));
    console.log(`Server ready at http://localhost:4000${server.graphqlPath}`);
    return { server, app };
}

startApolloServer();

启动完成后,访问http://localhost:4000/graphql就能看到一个grahql的查询界面了

# 其他客户端

# 图形客户端

好比我们的REST API有postman这样的接口调试工具一样(postman后面也会支持graphql),graphql也有很多的图形客户端: https://blog.bitsrc.io/13-graphql-tools-and-libraries-you-should-know-in-2019-e4b9005f6fc2

我用的是GraphiQL 和 Altair 。

Altair的颜值高一些,安装也简单,直接去官网下载安装包即可。

# 后端请求

有时候可能需要后端请求graphql接口,聪明的你已经发现了,它也是http请求,只是格式参数和REST API有些不一样。

比如在shell下,我们可以这样:

curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "{ hello }"}' \
http://localhost:4000/graphql

比如在node下,我们可以这样:

fetch('/graphql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
  body: JSON.stringify({query: "{ hello }"})
})
  .then(r => r.json())
  .then(data => console.log('data returned:', data));

fetch看起来可能有点啰嗦,那我们可以用现成的库,比如用graphql-request:

import { request, gql } from 'graphql-request'

const query = gql`
  {
    Movie(title: "Inception") {
      releaseDate
      actors {
        name
      }
    }
  }
`

request('https://api.graph.cool/simple/v1/movies', query).then((data) => console.log(data))

又或者在PHP下,我们可以封装一个方法:

public static function send_query(string $endpoint,
                                      string $query,
                                      array $variables = [],
                                      ?string $token = null)
    {
        $headers = ['Content-Type: application/json'];
        if ($token) {
            $headers[] = $token;
        }
        $content = ['query' => $query];
        if(!empty($variables)){
            $content['variables'] = $variables;
        }
        $data = file_get_contents($endpoint, false, stream_context_create([
            'http' => [
                'method' => 'POST',
                'header' => $headers,
                'content' => json_encode($content),
            ]
        ]));
        if($data){
            $data = json_decode($data,true);
            return $data;
        }else{
            $error = error_get_last();
            return $error;
        }
    }

# 查询

有了前面的准备工作,我们就可以开始测试了。 先来查个hello吧:

{
  hello
}

可以看到会返回一个结果:

{
  "data": {
    "hello": "Hello world!"
  }
}

OK,今天的介绍就到这里了???就这?我REST API完全可以hold住这种hello world的需求呀,要你何用?

OKK,不急,我们明天继续。