附錄四、GraphQL/Relay 初體驗

    GraphQL 的出現主要是為了要解決 Web/Mobile 端不斷增加的 API 請求所衍生的問題。由於 RESTful 最大的功能在於很有效的前後端分離和建立 stateless 請求,然而 RESTful API 的資源設計上比較偏向單方面的互動,若是有著複雜資源間的關聯就會出現請求次數過多,遇到不少的瓶頸。

    根據 的定義,GraphQL 是一個資料查詢語言和 runtime。Query responses 是由 client 所宣告決定,而非 server 端,且只會回傳 client 所宣告的內容。此外,GraphQL 是強型別(strong type)且可以容易使用階層(hierarchical)和處理複雜的資料關連性,並更容易讓前端工程師和產品工程師定義 Schema 來使用,賦予前端對於資料的制定能力。

    GraphQL 主要由以下元件構成:

    1. 類別系統(Type System)
    2. 查詢語言(Query Language):在 Operations 中 query 只讀取資料而 mutation 寫入操作
    3. 執行語意(Execution Semantics)
    4. 靜態驗證(Static Validation)
    5. 類別檢查(Type Introspection)

    一般 RESTful 在取用資源時會對應到 HTTP 中 、POSTDELETEPUT 等方法,並以 URL 對應的方式去取得資源,例如:

    取得 id 為 3500401 的使用者資料:

    GET /users/3500401

    以下則是 GraphQL 宣告的 query 範例,宣告式(declarative)的方式比起 RESTful 感覺起來相對直觀:

    接收到 GraphQL query 後 server 回傳結果:

    1. {
    2. "user" : {
    3. "id": 3500401,
    4. "name": "Jing Chen",
    5. "isViewerFriend": true,
    6. "profilePicture": {
    7. "uri": "http://someurl.cdn/pic.jpg",
    8. "width": 50,
    9. "height": 50
    10. }
    11. }
    12. }

    在 GraphQL 中有取得資料 Query、更改資料 Mutation 等操作。以下我們先介紹如何建立 GraphQL Server 並取得資料。

    1. 環境建置
      接下來我們將動手建立 GraphQL 的簡單範例,讓大家感受一下 GraphQL 的特性,在這之前我們需要先安裝以下套件建立好環境:

      1. graphql:GraphQL 的 JavaScript 實作.
      2. :Node web framework.
      3. express-graphql, an express middleware that exposes a GraphQL server.

        1. $ npm init
        2. $ npm install graphql express express-graphql --save
    2. 以下是 data.json

    3. Server 設計

      1. // 引入函式庫
      2. import graphql from 'graphql';
      3. import graphqlHTTP from 'express-graphql';
      4. import express from 'express';
      5. // 引入 data
      6. const data = require('./data.json');
      7. // 定義 User type 的兩個子 fields:`id` 和 `name` 字串,注意型別對於 GraphQL 非常重要
      8. const userType = new graphql.GraphQLObjectType({
      9. name: 'User',
      10. fields: {
      11. id: { type: graphql.GraphQLString },
      12. name: { type: graphql.GraphQLString },
      13. }
      14. });
      15. const schema = new graphql.GraphQLSchema({
      16. query: new graphql.GraphQLObjectType({
      17. name: 'Query',
      18. fields: {
      19. user: {
      20. type: userType,
      21. // 定義所接受的 user 參數
      22. id: { type: graphql.GraphQLString }
      23. },
      24. // 當傳入參數後 resolve 如何處理回傳 data
      25. resolve: function (_, args) {
      26. return data[args.id];
      27. }
      28. }
      29. }
      30. })
      31. });
      32. // 啟動 graphql server
      33. express()
      34. .use('/graphql', graphqlHTTP({ schema: schema, pretty: true }))
      35. .listen(3000);
      36. console.log('GraphQL server running on http://localhost:3000/graphql');

      在終端機執行:

      1. node index.js

      這個時候我們可以打開瀏覽器輸入 localhost:3000/graphql.,由於沒有任何 Query,目前會出現以下畫面:

    4. Query 設計

      當 GraphQL 指令為:

      將回傳資料:

      1. {
      2. "data": {
      3. "user": {
      4. "name": "Dan"
      5. }
      6. }
      7. }

      在了解了資料和 Query 設計後,這個時候我們可以打開瀏覽器輸入(當然也可以透過終端機 curl 的方式執行):
      http://localhost:3000/graphql?query={user(id:"1"){name}},此時 server 會根據 GET 的資料回傳:

      Relay/GraphQL 初體驗

    到這裡,你已經完成了最簡單的 GraphQL Server 設計了,若你遇到編碼問題,可以嘗試使用 JavaScript 中的 encodeURI 去進行轉碼。也可以自己嘗試不同的 Schema 和 Query,感受一下 GraphQL 的特性。事實上,GraphQL 還擁有許多有趣的特色,例如:Fragment、指令、Promise 等,若讀者對於 GraphQL 有興趣可以進一步參考 。

    Relay is a new framework from Facebook that provides data-fetching functionality for React applications.

    整個 Relay 架構流程圖:

    一般來說要使用 Relay 必須先準備好以下三項工具:

    1. A GraphQL Schema

    2. A GraphQL Server

    3. Relay

      • network layer:Relay 透過 network layer 傳 GraphQL 給 server

    接下來我們來透過 React 官方上的範例來讓大家感受一下 Relay 的特性。上面我們有提過:在 Relay 中可以讓每個 Component 透過 GraphQL 的整合處理可以更精確地向 Component props 提供取得的數據,並在 client side 存放一份所有數據的 store 當作暫存。所以,首先我們先建立每個 Component 和 GraphQL/Relay 的對應:

    1. // 建立 Tea Component,從 this.props.tea 取得資料
    2. class Tea extends React.Component {
    3. render() {
    4. var {name, steepingTime} = this.props.tea;
    5. return (
    6. <li key={name}>
    7. {name} (<em>{steepingTime} min</em>)
    8. </li>
    9. );
    10. }
    11. }
    12. // 使用 Relay.createContainer 建立資料溝通窗口
    13. Tea = Relay.createContainer(Tea, {
    14. fragments: {
    15. tea: () => Relay.QL`
    16. fragment on Tea {
    17. steepingTime,
    18. }
    19. `,
    20. },
    21. });
    22. class TeaStore extends React.Component {
    23. render() {
    24. return <ul>
    25. {this.props.store.teas.map(
    26. tea => <Tea tea={tea} />
    27. )}
    28. </ul>;
    29. }
    30. }
    31. TeaStore = Relay.createContainer(TeaStore, {
    32. fragments: {
    33. store: () => Relay.QL`
    34. fragment on Store {
    35. teas { ${Tea.getFragment('tea')} },
    36. }
    37. `,
    38. },
    39. });
    40. // Route 設計
    41. class TeaHomeRoute extends Relay.Route {
    42. static routeName = 'Home';
    43. static queries = {
    44. store: (Component) => Relay.QL`
    45. query TeaStoreQuery {
    46. store { ${Component.getFragment('store')} },
    47. }
    48. `,
    49. };
    50. }
    51. ReactDOM.render(
    52. <Relay.RootContainer
    53. Component={TeaStore}
    54. route={new TeaHomeRoute()}
    55. />,
    56. mountNode
    57. );

    GraphQL Schema 和 store 建立:

    限於篇幅,我們只能讓大家感受一下 Relay 的簡單範例,若大家想進一步體驗 Relay 的優勢,已經幫你準備好 GraphQL Server、transpiler 的 專案會是個很好的開始。

    React 生態系中,除了前端 View 的部份有革新性的創新外,GraphQL 更是對於資料取得的全新思路。雖然 GraphQL 和 Relay 已經成為開源專案,但技術上仍持續演進,若需要在團隊 production 上導入仍可以持續觀察。到這邊,若是一路從第一章看到這裡的讀者真的要給自己一個熱烈掌聲了,我知道對於初學者來說 React 龐大且有許多的新的觀念需要消化,但如同筆者在最初時所提到的,學習 React 重要的是透過這個生態系去學習現代化網頁開發的工具和方法以及思路,成為更好的開發者。根據前端摩爾定律,每半年就有一次大變革,但基本 Web 問題和觀念依然不變,大家一起加油啦!若有任何問題都歡迎來信給筆者或是發 ,當然 PR is welcome :)

    1. Your First GraphQL Server
    2. Learn GraphQL
    3. GraphQL 官網
    4. A reference implementation of GraphQL for JavaScript
    5. Node.js 服务端实践之 GraphQL 初探

    (image via 、kadira

    | |