使用React和Apollo来试用Github的GraphQL API
Livesense的第二篇《Advent Calendar 2016 – Qiita》的24号文章。现在我正在写这篇文章的时候已经是25号了(;・∀・)。
我会使用Apollo作为GraphQL客户端库来解释如何使用GraphQL API的示例代码。
因为Apollo也有示例代码,但是必须自己搭建服务器,非常麻烦。所以,通过方便地使用Github的GraphQL API,我认为可以了解一下这种感觉。
GraphQL是什么?
这个链接里的Qiita上有一篇关于GraphQL入门的文章,你可以参考一下。
源代码
在进行操作之前,请按照安装说明中所写的来进行设置。首先,需要在上述提到的Github的 Early Access Program 上注册,并按照“Creating an access token guide”获取一个访问令牌。请将该访问令牌保存至 config.secret.js 中。
请不要将此代码直接上传到互联网或在生产环境中使用类似的写法,因为此代码只是一个示例,直接包含了GitHub访问令牌在客户端代码中。
关于阿波罗
这次我们使用了Apollo这个GraphQL客户端库,其中使用了Apollo提供的适用于JavaScript的apollo-client(还有适用于Swift和Android的客户端)。此外,示例中使用了React,但也提供了与React绑定的工具,可以无缝地连接React组件和GraphQL。
尽管有一个由Facebook开发的名为Relay的GraphQL React客户端库,但是由于Relay的查询格式与标准GraphQL的格式不同,我们决定使用Apollo来完成这个需求。
解析示例代码
GraphQL查询
以下是GraphQL查询的部分。
import gql from 'graphql-tag'
:
const RepositorySearchQuery = gql`
query Search($q: String!) {
search(query: $q, first: 10, type: REPOSITORY) {
edges {
node {
... on Repository {
id,
name,
url
}
}
}
}
}
`
gql是Apollo提供的用于表示GraphQL查询的库函数,查询主体位于“quer Search($q: String!) {…}”的部分。这个描述的意象就像直接写SQL一样。这部分可以直接传递给GraphQL的GUI客户端,实际上,操作下面的结果会得到如下所示的结果。

用于连接API的客户端
import ApolloClient, { createNetworkInterface } from 'apollo-client'
:
const networkInterface = createNetworkInterface({ uri: 'https://api.github.com/graphql' }) // (1)
networkInterface.use([{ // (2)
applyMiddleware(req, next) {
if (!req.options.headers) {
req.options.headers = {
}
req.options.headers.authorization = `bearer ${githubToken}`
next()
}
}])
const client = new ApolloClient({ // (1)
networkInterface: networkInterface,
})
创建连接到API的客户端,步骤如上。
使用helper函数createNetworkInterface生成NetworkInterface对象,并将其传递给client(1)。
createNetworkInteface函数会默认生成一个用于访问同一域名下的/graphql路径的NetworkInteface对象,所以若要连接到其他域名的API或者当前GraphQL路径非/graphql的情况下,需要指定uri选项。
此外,如果像Github API那样需要在HTTP请求中添加Authorization头部的情况下,可以通过NetworkInterface的use方法来添加一个用于在HTTP请求头部中添加Authorization头部的中间件(2)。(就像express一样,NetworkInterface可以使用use方法来叠加用于拦截HTTP请求的中间件)
将React组件与GraphQL进行集成
import { ApolloProvider, graphql } from 'react-apollo'
:
class Repositories extends Component { // (3)
render() {
let repos = []
let h1Text = `Searching For ${this.props.query} ...`
if (this.props.data && this.props.data.search) { // (5)
h1Text = `Repositories about ${this.props.query} are`
repos = this.props.data.search.edges.map(edge => ({
key: edge.node.id,
name: edge.node.name,
url: edge.node.url
}))
}
return (
<div>
<h1>{h1Text}</h1>
<ul>
{repos.map(repo => <Repository key={repo.key} name={repo.name} url={repo.url} />)}
</ul>
</div>
)
}
}
class App extends Component {
:
:
handleFormSubmit(evnt) {
evnt.preventDefault()
const RepositoriesWithQuery = graphql(RepositorySearchQuery, { // (4)
options: {variables: { q: this.state.q }}
})(Repositories)
this.setState({
repositories: <RepositoriesWithQuery query={this.state.q} />
})
}
:
render() {
return (
<div>
<SearchForm onSubmit={evnt => this.handleFormSubmit(evnt)} onChange={evnt => this.handleTextChange(evnt)} />
<ApolloProvider client={client}> // (6)
{this.state.repositories}
</ApolloProvider>
</div>
)
}
}
这个React组件与(3)的存储库现在与GraphQL相关联。
通过graphql函数(由react-apollo提供的辅助函数)将其与GraphQL查询关联起来,就像(4)中那样。具体来说,graphql函数返回的结果也是React组件,该组件通过props将从API获取的数据传递给此组件,这样就能在Component中传播数据,如(5)所示。通过props.data,GraphQL的结果将传递过来。由于props.data以下的结构完全与从GraphQL API返回的JSON结构相同,因此您可以访问GUI客户端捕获的数据并以相同的结构获取所需的数据。而且,GraphQL的获取是异步执行的,因此与组件的初次渲染时间不同,所以通过对数据的存在与否进行条件分支判断(虽然我认为还有更好的写法,但这是一个示例代码嘛)。
最后是ApolloProvider组件,这是由react-apollo提供的,对于使用图书馆的用户来说,相当于一个魔法咒语。必须将它与GraphQL组件相关联,并将其放置在ApolloProvider组件的下方,这样才能正确地获取结果。
应该会像这样多分动起来吧…
最终最后
由于关于GraphQL的日语文章不是很多,所以我认为它并没有引起太多的关注,但如果在本文中有错误或奇怪的解释,请您指正。我希望GraphQL能在日本更加普及,所以我打算在未来继续撰写相关文章。谢谢。
无法连接到FunctionalComponent。↩
实际上,props.data中包含了GraphQL结果JSON的信息以及一些元数据和函数。另外,您可以通过将选项传递给graphql函数来修改props.data的结构。详细信息请参阅Apollo的文档。↩