GraphQL 简介 GraphQL 是一种用于 API 调用的数据查询语言,由 Facebook 开发并开源。传统的 API 调用一般获取到的是后端组装好的一个完整对象,而前端可能只需要用其中的某些字段,大部分数据的查询和传输工作都浪费了。GraphQL 提供了一种全新数据查询方式,可以只获取需要的数据,使api调用更灵活、高效和低成本。
GraphQL 的主要特点包括:
需要什么就获取什么数据
支持关系数据的查询
API无需定义各种路由,完全数据驱动
无需管理API版本,一个版本持续演进
支持大部分主流开发语言和平台
强大的配套开发工具
使用方法 下面我们通过搭建一个 SpaceX 的新闻网站来直观学习graphQL的基本使用方法,所有数据由官方 API 获得。
服务端 服务端采用 node
+ express
搭建。新建一个 node 项目,安装如下依赖:
1 $ npm i graphql express-graphql express axios
创建入口文件 server.js
,里面创建 express 服务。使用 graphQL 我们只需要设置一个路由,所有的请求都由这个 graphQL 的 request handler 处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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
可以在浏览器中直接对 graphQL 进行调试。更多 express-graphql 的用法请参考 Github express-graphql。
schema 接下来我们定义 schema,schema 意为模式,其中定义了数据模型的结构、字段的类型、模型间的关系,是 graphQL 的核心。
新建 schema.js
文件,首先定义两个数据模型:LaunchType(发射)和 RocketType(火箭)。注意字段的数据类型需要使用 GraphQL 定义的,不能使用 js 中的基本数据类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 RocketType = new GraphQLObjectType({ name: 'Rocket' , fields: () => ({ rocket_id: { type : GraphQLString }, rocket_name: { type : GraphQLString }, rocket_type: { type : GraphQLString } }) });
有了数据模型之后,我们需要从数据库或者第三方 API 获取数据,在此我们从 spacex 的官方 API 获取。我们需要定义一个 root query,root query 做为所有查询的入口,处理并返回数据。。
在 schema.js
中增加代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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
:
或者更多的属性:
是不是很简单很神奇!
单个查询 我们也可以通过传入参数查询单条信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 项目:
1 $ npx create-react-app client
为了便于调试,在 package.json
中增加 scripts:
1 2 3 4 "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 客户端程序。
安装依赖 安装如下依赖:
1 2 $ cd client $ npm i apollo-boost react-apollo graphql
其中:
apollo-boost
是 apollo client 本身
react-apollo
是 react 视图层的集成
graphql
用于解析 graphql 的查询语句
设置 client 修改 App.js
内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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
:
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 33 34 35 36 37 38 39 40 41 42 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
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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> ) }
查询语句通过 graphql-tag
定义,然后传入 <Query>
执行。
运行 由于本地调试,client 和 server 分别运行在不同的端口,所以需要先进行跨域处理,使用 cors。
server.js
代码如下:
1 2 3 4 5 const cors = require ('cors' );... app.use(cors());
最终效果 好了,大功告成,我们来看一下效果:
结束语 今天就主要介绍 GraphQL 工程的搭建和 GraphQL Query 的使用,更多关于 GraphQL 的内容,比如 Mutation
下次有机会再和大家逐步讲解。
本文参考资料: