GraphQL 入門詳解

  • 2019 年 10 月 10 日
  • 筆記

作者:MudOnTire github 地址:https://github.com/MudOnTire

簡介

定義

一種用於API調用的數據查詢語言

核心思想

傳統的api調用一般獲取到的是後端組裝好的一個完整對象,而前端可能只需要用其中的某些欄位,大部分數據的查詢和傳輸工作都浪費了。graphQL提供一種全新數據查詢方式,可以只獲取需要的數據,使api調用更靈活、高效和低成本。

特點

  1. 需要什麼就獲取什麼數據
  2. 支援關係數據的查詢
  3. API無需定義各種路由,完全數據驅動
  4. 無需管理API版本,一個版本持續演進
  5. 支援大部分主流開發語言和平台
  6. 強大的配套開發工具

使用方法

下面我們通過搭建一個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可以在瀏覽器中直接對graphQL進行調試。更多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>    )  }

查詢語句通過graphql-tag定義,然後傳入<Query>執行。

運行

由於本地調試,client和server分別運行在不同的埠,所以需要先進行跨域處理,使用 cors。

server.js

const cors = require('cors');    ...    app.use(cors());

效果

好了,大功告成,我們來看一下效果:

結語

今天就主要介紹GraphQL工程的搭建和GraphQL Query的使用,更多關於GraphQL的內容比如 Mutation下次有空會跟大家逐步講解。

本文靈感來源:Youtube@Traversy Media,感謝

本文Demo Github地址:Github@MudOnTire

本文Demo線上展示:Heroku@graphql-spacex-launches