乐趣区

GraphQL 入门详解

简介
定义
一种用于 API 调用的数据查询语言
核心思想
传统的 api 调用一般获取到的是后端组装好的一个完整对象,该对象的结构相对固定,而且前端可能只需要用其中的某些字段,大部分数据的查询和传输工作都浪费了。graphQL 提供一种全新数据查询方式,可以只获取需要的数据,使 api 调用更灵活、高效和低成本。
特点

需要什么就获取什么数据
支持关系数据的查询
API 无需定义各种路由,完全数据驱动
无需管理 API 版本,一个版本持续演进
支持大部分主流开发语言和平台
强大的配套开发工具

使用方法
下面我们通过搭建一个 SpaceX 的新闻网站来直观学习 graphQL 的基本使用方法,所有数据由 官方 API 获得。
服务端
服务端采用 node + express。新建一个 node 项目,安装如下依赖:
$ npm i graphql express-graphql express axios
创建入口文件 server.js,里面创建 express 服务。使用 graphQL 我们只需要设置一个路由,所有的请求都由这个 graphQL 的 request handler 处理:
const express = require(‘express’);
const graphqlHTTP = require(‘express-graphql’);
const schema = require(‘./schema’);

const app = express();

app.use(‘/graphql’, graphqlHTTP({
schema,
graphiql: true
}));

const PORT = process.env.PORT || 5000;

app.listen(PORT,()=>console.log(`Server started on port ${PORT}`));
graphqlHTTP 是 grapql 的 http 服务,用于处理 graphql 的查询请求,它接收一个 options 参数,其中 schema 是一个 GraphQLSchema 实例,我们待会儿定义。graphiql 设置为 true 可以在浏览器中使用 GraphiQL 直接进行调试,之后会看到。更多 express-graphql 的用法请参考 Github express-graphql。
schema
接着我们定义 schema,schema 意为‘模式’,其中定义了数据模型的结构、字段的类型、模型间的关系,是 graphQL 的核心。
新建 schema.js 文件,定义两个数据模型:LaunchType(发射)和 RocketType(火箭)。注意字段的数据类型需要使用 GraphQL 定义的,不能使用 js 中的基本数据类型。
const {GraphQLObjectType, GraphQLInt, GraphQLString, GraphQLBoolean, GraphQLList, GraphQLSchema} = require(‘graphql’);

const LaunchType = new GraphQLObjectType({
name: ‘Launch’,
fields: () => ({
flight_number: {type: GraphQLInt},
mission_name: {type: GraphQLString},
launch_date_local: {type: GraphQLString},
launch_success: {type: GraphQLBoolean},
rocket: {type: RocketType},
})
});

const LaunchType = new GraphQLObjectType({
name: ‘Rocket’,
fields: () => ({
rocket_id: {type: GraphQLString},
rocket_name: {type: GraphQLString},
rocket_type: {type: GraphQLString}
})
});
有了数据模型之后,我们需要从数据库或者第三方 API 获取数据,在此我们从 spacex 的官方 API 获取。我们需要定义一个 root query,root query 将做为所有查询的入口,处理并返回数据,更多请参考 GraphQL Root fields & resolvers。
在 schema.js 中增加代码:
const axios = require(‘axios’);

const RootQuery = new GraphQLObjectType({
name: ‘RootQueryType’,
fields: {
launches: {
type: new GraphQLList(LaunchType),
resolve(parent, args) {
return axios.get(‘https://api.spacexdata.com/v3/launches’).then(res => res.data);
}
}
}
});

module.exports = new GraphQLSchema({
query: RootQuery
});
查询列表
完成这一步,api 服务基本搭建完成!我们看一下效果,在浏览器中输入 http://localhost:5000/graphql 将打开 Graphiql(生产环境建议禁用):

我们可以只查询所有的 flight_number:

或者更多的属性:

是不是很简单很神奇!
单个查询
我们也可以通过传入参数查询单条信息:
const RootQuery = new GraphQLObjectType({
name: ‘RootQueryType’,
fields: {

launch: {
type: LaunchType,
args: {
flight_number: {type: GraphQLInt}
},
resolve(parent, args) {
return axios.get(`https://api.spacexdata.com/v3/launches/${args.flight_number}`)
.then(res => res.data);
}
}
}
});
结果:

前端
刚刚我们都是用 GraphiQL 在浏览器调用接口,接下来我们看一下在前端页面中怎么调用 graphql 服务。前端我们使用 react。
在项目根目录初始化 react 项目:
$ npx create-react-app client
为了便于调试,在 package.json 中增加 scripts:
“start”: “node server.js”,
“server”: “nodemon server.js”,
“client”: “npm start –prefix client”,
“dev”:”concurrently \”npm run server\” \”npm run client\” ”
样式我们使用 bootswatch 中的一款主题:

GraphQL 的客户端有多种实现,本次项目使用 Apollo,最流行的 GraphQL Client。更多 client 请参考 GraphQL Clients。
安装依赖
安装如下依赖:
$ cd client
$ npm i apollo-boost react-apollo graphql

其中 apollo-boost 是 apollo client 本身,react-apollo 是 react 视图层的集成,graphql 用于解析 graphql 的查询语句。
设置 client
修改 App.js 内容如下:
import React, {Component} from ‘react’;
import ApolloClient from ‘apollo-boost’;
import {ApolloProvider} from ‘react-apollo’;
import ‘./theme.css’;
import ‘./App.css’;
import logo from ‘./spacex-logo-light.png’

const client = new ApolloClient({
uri: ‘http://localhost:5000/graphql’
});

class App extends Component {
render() {
return (
<ApolloProvider client={client}>
<div className=”container”>
<img src={logo} id=”logo” />
</div>
</ApolloProvider>
);
}
}

export default App;

和 redux 使用 <Provider> 传递 store 类似,react-apollo 通过 <ApolloProvider> 将 apollo client 向下传递。
实现 query
接着我们来实现显示 launches 的 component,新增文件 components/Launches.js:
import React, {Component, Fragment} from ‘react’;
import gql from ‘graphql-tag’;
import {Query} from ‘react-apollo’;
import LaunchItem from ‘./LaunchItem’;

const LAUNCHES_QUERY = gql`
query LaunchesQuery {
launches {
flight_number,
mission_name,
launch_date_local,,
launch_success,
}
}
`;

export class Launches extends Component {
render() {
return (
<Fragment>
<h1 className=”display-4 my-3″>Launches</h1>
<Query query={LAUNCHES_QUERY}>
{
({loading, error, data}) => {
if (loading) return <h4>Loading…</h4>
if (error) console.log(error);
return (
<Fragment>
{
data.launches.map(launch => <LaunchItem key={launch.flight_number} launch={launch}/>)
}
</Fragment>
)
}
}
</Query>
</Fragment>
)
}
}

export default Launches
query 语句通过 graphql-tag 定义,传入 <Query> 执行获取数据并传入 LaunchItem 显示。
components/LaunchItem.js:
import React from ‘react’

export default function LaunchItem({launch: { flight_number, mission_name, launch_date_local, launch_success} }) {
return (
<div className=”card card-body mb-3″>
<div className=”col-md-9″>
<h4>Mission: {mission_name}</h4>
<p>Date: {launch_date_local,}</p>
</div>
<div className=”col-md-3″>
<button className=”btn btn-secondary”>Launch Details</button>
</div>
</div>
)
}
运行
由于本地调试,client 和 server 分别运行在不同的端口,所以需要先进行跨域处理,使用 cors。
server.js
const cors = require(‘cors’);

app.use(cors());
效果
好了,大功告成,我们来看一下效果:

结语
今天就主要介绍 GraphQL 工程的搭建和 GraphQL Query 的使用,更多关于 GraphQL 的内容比如 Mudtation 下次有空会跟大家逐步讲解。
本文灵感来源:Youtube@Traversy Media,感谢
本文完整 demo(增加部分功能):Github@MudOnTire

退出移动版