<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          在小程序里接入 GraphQL

          共 4833字,需瀏覽 10分鐘

           ·

          2021-10-03 22:34

          背景

          在前幾篇文章里,一直在講 GraphQL。分別是:



          BFF 是 Backend For Frontend 的簡稱,是為前端服務(wù)的后端。要發(fā)揮真正的用處,還得通過前端體現(xiàn)?,F(xiàn)在就給個實例,講解如何在小程序里集成萬能 BFF。


          前端主要有 Web、小程序以及 Native App 等。要在前端集成 GraphQL,一般采用 Apollo Client。為什么本文選擇小程序作為例子呢?因為小程序是中國特色,國外沒有。對于如何在 Web 端或者 Native App 端集成 GraphQL,只需要按照 https://www.apollographql.com/docs/ 官方文檔的指導(dǎo)去做即可。


          3eca453c2af322b1b0d0329918668c5e.webp


          采用小程序作為例子,不僅彌補了官方文檔的空白,而且由于小程序的一些限制,在集成 GraphQL 的過程中,還面臨一些額外的挑戰(zhàn)。因為更困難,所以更加符合本公眾號(哈德韋,即 Hard Way 的音譯)的初衷。


          最終成果演示

          • ? ? ? Web 版:https://taro.pa-ca.me/

          • ? ? ? ?微信小程序(體驗版):

          ??????可以微信掃碼打開小程序體驗版(由于還沒有發(fā)布,因此只能以體驗的形式),申請體驗。我看到申請請求后會第一時間通過,有 100 名的限制哦,如果因為人數(shù)超限不能體驗,那么請等待我的下一篇文章,如果哈德韋微信小程序正式發(fā)布上線,我會再發(fā)文通知大家。


          項目源代碼

          • ? ? ? ?https://github.com/Jeff-Tian/weapp



          Taro Js


          盡管這里只做了微信小程序,但是采用了多端統(tǒng)一開發(fā)框架 Taro Js,從而可以打包到不同的平臺。


          挑戰(zhàn)一:在小程序里生成 Apollo Client 實例


          基本可以參考官方文檔的 React 示例,但是對于小程序,卻不能照搬。如果只做 Web 端,可以完全參考官網(wǎng)文檔的 React 示例,只需要傳入一個 GraphQL 服務(wù)的 url 即可。但是對于小程序,只穿 url 會報錯,原因是,對于小程序,缺少默認(rèn)的全局 fetch 函數(shù),因此在生成 GraphQL 客戶端實例時,要額外傳遞自定義的 fetch。當(dāng)然,對于使用了 Taro js 的項目,只需要將 Taro.request 封裝一下就好。


          從而最終的 GraphQL 客戶端實例的生成是這樣的:


          import {ApolloClient, HttpLink, InMemoryCache} from "@apollo/client"import Taro from "@tarojs/taro"

          export const client = new ApolloClient({

          link: new HttpLink({ uri: `https://uniheart.pa-ca.me/proxy?url=${encodeURIComponent('https://jqp5j170i6.execute-api.us-east-1.amazonaws.com/dev/gatsby/graphql')}`,

          async fetch(url, options) { const res = await Taro.request({ url: url.toString(), method: 'POST', header: { 'content-type': 'application/json' }, data: options?.body, success: console.log })

          return {text: async () => JSON.stringify(res.data)} as any } }), cache: new InMemoryCache()})


          挑戰(zhàn)二:允許小程序訪問 GraphQL 服務(wù)


          從上面的代碼中可以看到這個 URL:https://jqp5j170i6.execute-api.us-east-1.amazonaws.com/dev/gatsby/graphql,這就是上一篇文章《使用萬能 BFF,將語雀文章 GraphQL 服務(wù)化

          》的最終成果,它將語雀博客作為數(shù)據(jù)源,通過 AWS lambda 暴露成為一個 GraphQL 服務(wù)。


          而這個長長的 URL

          https://jqp5j170i6.execute-api.us-east-1.amazonaws.com/dev/gatsby/graphql 就是利用 serverless 自動創(chuàng)建的 AWS API Gateway 的默認(rèn) URL。


          直接使用 Taro.request 訪問它,在打包成為小程序后,執(zhí)行到這里就會報錯,原因是沒有把這個域名配置在白名單里。


          這可以嘗試通過小程序后臺配置 request 合法域名解決:


          130880c8d5a29b45c665b58ebbb79088.webp


          挑戰(zhàn)三:域名備案


          一個偷懶的做法,就是將 AWS API Gateway 的默認(rèn)域名填進去,結(jié)果發(fā)現(xiàn)通不過域名備案檢查!


          7ecfce44bc3c83a523fa6ed277d4eddc.webp


          挑戰(zhàn)四:Serverless 自定義域名


          當(dāng)然沒有辦法給 AWS 生成的域名去備案,但是可以不要用 AWS API Gateway 自動生成的域名,而是指定一個自定義域名,將這個自定義域名備案。


          由于我們的 lambda 使用了 serverless 框架自動化,要使用自定義域名,可以簡單地通過增加一個 serverless 插件:serverless-domain-manager 來幫助我們自動關(guān)聯(lián)這個自定義域名。利用這個插件,可以自動生成 AWS Route 53,以及關(guān)聯(lián)相應(yīng)的 Gateway。


          然而,真要這樣做,需要去 AWS 上購買域名,或者將自己的域名過戶到 AWS 的控制臺。這……


          總之看起來要使用自定義域名,不那么友好,可能還需要產(chǎn)生額外的費用,那這個就沒意思了。


          挑戰(zhàn)五:轉(zhuǎn)發(fā) GraphQL 請求


          出于節(jié)省成本的考慮,以及盡可能最大化復(fù)用已有服務(wù),決定使用轉(zhuǎn)發(fā) GraqphQL 請求的方式。這里介紹下前情提要,我早些年備案了一個域名:pa-ca.me,并且在這上面部署了一個服務(wù):https://uniheart.pa-ca.me ,該項目源代碼在這里:https://github.com/Jeff-Tian/alpha


          聽說有的大神,可以盲寫代碼直接上線一次過,沒有 BUG。這真令人羨慕,不過我今天也感受了一次一把過,即給已有項目 https://github.com/Jeff-Tian/alpha 添加了一個轉(zhuǎn)發(fā) GraqphQL 請求的新功能,一次提交,自動發(fā)布后就可以使用了,效率實在令自己滿意:https://github.com/Jeff-Tian/alpha/commit/e58dcf0e7f80643b192561e795bbb3cf050993fd。至今沒有發(fā)現(xiàn) BUG,是真的很神嗎?其實不是,只是有一個好習(xí)慣而已:


          測試先行


          這個已有項目是基于 eggjs 的,eggjs 其實還是有些坑的,即在發(fā)送請求時,有一個 contentType 選項,對于發(fā)送 POST 請求(GraphQL 查詢本質(zhì)上是一個 HTTP Post 請求),我相信多數(shù)開發(fā)都會自然設(shè)置 contentType = "application/json",但是在 eggjs 生態(tài)里,這樣設(shè)置是沒有效果的,會導(dǎo)致 GraphQL 服務(wù)收不到請求 body。這一次新功能的添加,之所以一次部署就能過,其實是因為在改代碼前,先寫好了自動化測試。即先寫好了一個期待的轉(zhuǎn)發(fā)功能的正確表現(xiàn),然后運行,讓測試失敗。然后寫實現(xiàn)代碼,再次運行測試,直到測試通過。這其中并沒有想象中的那么順利,需要嘗試各種請求選項的設(shè)置,知道找到一個(或者幾個)能夠工作的設(shè)置組合。自動化測試的好處是讓我的嘗試可以很快得到驗證。


          測試用例




          const graphql = async () => { const res = await app.httpRequest() .post(`/proxy?url=${encodeURIComponent('https://jqp5j170i6.execute-api.us-east-1.amazonaws.com/dev/gatsby/graphql')}`) .type('application/json') .send({ query: '{ \n yuque(id: "53296538") {\n id\n title\n description\n \n }\n \n allYuque {\n nodes {\n id\n title\n }\n }\n}', variables: null }) .expect(200);

          assert.strictEqual(res.body.errors, undefined); assert(res.body.data.yuque.title === '快速下載 GitHub 上項目下的子目錄'); };

          it('should proxy graphql', graphql);


          最終實現(xiàn)


           subRouter.post('/', controller.proxy.proxy.post);

          public async post() { const { ctx } = this;

          const { data } = (await ctx.curl(ctx.query.url, { streaming: false, retry: 3, timeout: [ 3000, 30000 ], method: 'POST', type: 'POST', contentType: 'json', data: ctx.request.body, dataType: 'json', }));

          ctx.body = data; }


          可見這個 contentType 必須設(shè)置成 json,才能觸發(fā) ctx.curl 以及其底層的 urlib 自動將 header 中的 content-type 設(shè)置成 application/json !


          至此,就解釋清楚了挑戰(zhàn)一中,為什么生成 apollo 客戶端實例時,會有一個 proxy 的 url 出現(xiàn)了。這一切彎彎繞繞都是因為小程序的限制,如果你足夠有錢,可以接受在 AWS Route 53 里再申請一個域名,那么這一切可以得到一些簡化。


          總結(jié)


          本文給萬能 BFF 最終在前端的使用舉了一個例子,詳解了如何在小程序中接入 GraphQL。因為利用了 TaroJs,所以同步部署了一個 Web 端:https://taro.pa-ca.me。Web 端的集成只需要參考 Apollo 官方文檔,因此沒有贅述其實現(xiàn),而是找了一個相對更難的實現(xiàn)方案:微信小程序。這不僅彌補了官方示例的空白,而且體現(xiàn)了中國特色。


          瀏覽 80
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩美女A片 | 亚洲有码电影 | 四虎影库成人精品 | 97国产精品| 7799天天综合网 |