[笔记]Apollo 初体验

趁着 GitHub 的 Api 转向 GraphQL 时, 了解了下相关知识和其中一个蛮有名气的实现: Apollo.

先简单的唠叨两句, 相对比 Rest 和 GraphQL, 我觉得后者比较适合 Full-stack 框架的设计. 前端定义好的 SQL 可以完美对应 FE 的 model 层以及生成 PropTypes 或是 TypeScript 的类型定义. 而且 web app 接口的变化大多时候是前端驱动的, GraphQL 的特点之一, 业务带来的字段与查询变化, 开发人员可以优先专注前端, generate 出相应的接口与数据模型.

发布时, 如果前后端的规则一致, 也可以对两端同时编译, 将前端 GraphQL 的细节抽象, 转换为类似 Rest 的接口形式. 避免了请求包含字段细节从而增加安全性, 以及不用去烦恼前后端通讯的 graphQL 大小.

说到底, 想要发挥 GraphQL 的优势, 需要一套能 carry 全场的 graphql-generator 工具, 然而凭借我(两三天)对 graphql 的了解, 目前尚没有发现特别通用的工具.


简单的 Apollo 调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import ApolloClient, { createNetworkInterface } from 'apollo-client';
import gql from 'graphql-tag';

export default function() {
const client = new ApolloClient({
networkInterface: createNetworkInterface({
uri: 'http://api.githunt.com/graphql'
})
});

client.query({
query: gql`{
feed (type: TOP, limit: 10) {
repository {
name, owner { login }

# Uncomment the line below to get number of stars!
# stargazers_count
}

postedBy { login }
}
}`
})
.then(data => console.log(data))
.catch(error => console.error(error));
}

client = new ApolloClient() 创建实例初始化接口地址, 通过 client.query 进行请求, 和 rest 之类接口用 fetch 请求并无二致. 不同的是请求的参数是一个 gql() 对象, 内部的字符串就是 GraphQL 的查询语句(字符串)

关于 GraphQL 的具体用法(查询字段, 增改等)可以参考GraphQL 名词 101:解析 GraphQL 的查询语法深入理解 GraphQL

React 中使用

Apollo 提供了 react-apollo 模块可以方便的在 React 中引入 Apollo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { gql, ApolloClient, createNetworkInterface, ApolloProvider, graphql } from 'react-apollo';

const Feed = ({ data }) => {
const { loading, feed = [] } = data;
if (loading) {
return <div>loading...</div>
}
return (
<ul>
{feed.map((item, i) => <Item key={i} item={item} />)}
</ul>
)
}

const FeedWithData = graphql(gql`{
feed (type: TOP, limit: 10) {
repository {
name, owner { login }

# Uncomment the line below to get number of stars!
# stargazers_count
}

postedBy { login }
}
}`)(Feed);

const App = () => (
<ApolloProvider client={createClient()}>
<FeedWithData />
</ApolloProvider>
);

通过 ApolloProvider 向组件注册了 ApolloClient, 通过高阶组件 FeedWithData 请求 GraphQL 查询内容, 注入到容器组件 Feed 中.

这个例子比较简单, 稍微扩展一下就可以根据 FeedWithData 内 GQL 请求的字段, 导出 Feed 里 props.data 的 PropTypes.

当然也有 for angular 以及 for iosfor Android 等等的实现. 官网没有 Vue 的例子感觉蛮尴尬的, 不过随便搜一搜可以找得到第三方实现.

服务端实现

Apollo 针对流行框架提供了一系列的中间件, 这里以 koa(v2+) 的 apollo-server-koa 为例:

1
2
3
4
5
6
const { graphqlKoa, graphiqlKoa } = require('apollo-server-koa');

router.get('/graphql', graphqlKoa({ schema: myGraphQLSchema }));
router.post('/graphql', graphqlKoa({ schema: myGraphQLSchema }));
router.get('/graphiql', graphiqlKoa({ endpointURL: '/graphql' }));
router.post('/graphiql', graphiqlKoa({ endpointURL: '/graphql' }));

graphqlKoa 会创建一个 graphql 服务, graphiqlKoa 会创建一个方便调试 graphql 的页面.

graphql-tools 库提供的 makeExecutableSchema 用于创建 graphql 的 scheme.

1
2
3
4
5
6
7
const { makeExecutableSchema } = require('graphql-tools');
const typeDefs = `
type Query {
testString: String
}
`;
const schema = makeExecutableSchema({ typeDefs });

graphql-tools 还提供了 addMockFunctionsToSchema 函数用于一键注入 mock 数据, 只需要一行代码就可以让 graphql 查询语句返回类似 hello world 之类的数据:

1
2
const { addMockFunctionsToSchema } = require('graphql-tools');
addMockFunctionsToSchema({ schema });

如果想要控制返回数据的内容, 我们需要用到 resolvers.

每一个 GraphQL 的字段对应一个 resolver 函数, 用于告诉 GraphQL 查询时需要返回的内容.

resolvers 的规则如下:

1
2
3
4
5
6
7
8
const resolvers = {
Query: {
testString(){
return 'Hello World';
},
},
}
makeExecutableSchema({ typeDefs, resolvers });

连接数据库

按照官方 Tutorial: How to build a GraphQL server 的说法, graphQL 连接数据库的操作应该放在 resolvers 中.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const resolvers = {
Query: {
feed: (root: any, args: any) => {
const { limit } = args;
return myModel.findAll({
attributes: ['id', 'name'],
limit: Math.min(10, limit)
}).then((data: any[]) => data.map(({id, name}) => ({
id,
name,
})));
},
},
};

上面的 myModel 是一个 sequelizemodel. 对于增删改类型的变化同理, 针对 GraphQL 的 mutation 去写 resolver 即可.

最后

以上内容是我最近在学习 GraphQL 和 Apollo 的一些笔记. 通过上面的内容可以简单的实现一套完整的 backend + frontend 系统.

当然, 这样简单的实现我不认为对生产效率能有多大帮助, 重要的是一套完整的前端静态类型与 GraphQL, GraphQL 与 Scheme, 以及 Database 之间的生成工具.

除了 Apollo 之外 GraphQL 还有一个更有名气的实现: Relay. 后者 GitHub 上的 star 数要多的多. 而且 GraphQLRelay 都是 Facebook 一家的东西. 其实我从 Apollo 出发去了解 GraphQL, 也是因为偶然间看到了一篇文章 Reducing our Redux code with React Apollo, 至于 ApolloRelay 的对比… 放在之后的笔记里吧…