1. 程式人生 > >一種不錯的 BFF Microservice GraphQL/REST API 層的開發方式

一種不錯的 BFF Microservice GraphQL/REST API 層的開發方式

![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140118576-1541099959.png) 雲原生(`Cloud Native`)Node JS Express Reactive 微服務模板 (`REST/GraphQL`) 這個專案提供了完整的基於 `Node JS / Typescript` 的微服務模板,包括生產部署、監控、除錯、日誌記錄、安全、CI/CD 所需的所有功能。還添加了基於響應性擴充套件的示例,以演示如何將其用於構建微服務 `API` 邊緣服務(`edge-service`)、前端的後端(`BFF`)或將其用作構建任何型別微服務的基礎。 專案地址:[nxplorerjs-microservice-starter](https://github.com/ERS-HCL/nxplorerjs-microservice-starter) ## 設計原則 - 使用同類最佳的模組來建立可用於生產的微服務框架 - 基於 [12-factor app](https://12factor.net/) 原則 - 沒有定製程式碼或包裝器,因此任何開發人員都可以修改/替換任何模組或實現 - 可作為參考的實現的設計模式 - 模組化,可替換和即插即用程式碼 - 為業務 API 和微服務平臺開發提供入門 - DevOps 準備了程式碼質量,單元和整合測試,自動部署。 ## 功能/特性 - `Node JS`, `Express`, `Typescript` , 依賴注入(Dependency Injection base) - 基於 `Backpack` (webpack) - 構建 , 開發 , 打包 - 啟用 `Swagger` - Express swagger 中介軟體 / Swagger UI 整合 - GraphQL 基於 `Apollo Server 2.0`,帶有 `JWT` 安全性、資料載入器(`data loader`)和 `REST` 資料來源示例 - 通過 `graphql-import` 支援 `GraphQL SDL` - 開發期間 `GraphQL mock resolvers` (可選) - `graphql-tools` - 基於 GraphQL 的客戶端包裝 API - `graphql-request` - REST APIs - 使用 `Inversify Controller` - 外部化配置 - `DotEnv` (設定,特定於 Env 的 API URL) - 測試 - `Jest` , `SuperTest` , `GraphQL Tester`。 自動化單元和整合測試的基礎設施 - 程式碼覆蓋率 - `Istanbul` - 程式碼質量 - `tslint` - 容器支援 - `Docker` , `Kubernetes` 叢集 - 基於 `Helm Chart` 的部署支援 - `Prometheus` 整合 - API 響應日誌記錄,Express Server 日誌記錄,UUID 傳播 - `Pino` - Reactive Extensions 支援 - `RxJS6` - CORS, JSONObject 限制 , Helmet , CSRF - Express 安全 - 基於 IOC / 依賴注入 / Express 註釋的 API - `Inversify` - 文件 - `TypeDocs` - API 異常處理實用程式 - 標準 HTTP Code 以獲取更清蒸的 code - 示例 API,模式以供參考 - `Sonar Qube` 整合 - `Hystrix` 熔斷器支援 (使用 Brakes) - 基於 JWT 的 API 安全性 - `jsonwebtoken`, `express-jwt` - 現在使用超級快速的 `pino` 日誌程式來滿足所有的日誌記錄需求 - 內建額外的效能時間記錄 檢視 REST API /examples/{id} ```json { "pid": 3984, "hostname": "LP-507B9DA1D355", "level": 30, "time": 1515813665734, "0": { "socket": 5.656709999995655, "lookup": 186.8375229999947, "connect": 389.5646870000055, "response": 594.8022639999981, "end": 599.1270230000082 }, "v": 1 } ``` ### REST APIs - 可以使用以下 URL 下載 API 規範 ``` http://localhost:3000/api-docs/Api.yaml ``` ``` http://localhost:3000/api-docs/ ``` - examples - API 中的按 ID 示例搜尋的基本示例 (`/examples/:id`) - shop - 如何使用 Reactive Extensions 進行 API 編排(`FlatMap`)的示例 (`/shop/priceByOptionId/:id`) - starwars - 如何使用 Reactive Extensions 進行 API 編排的示例(`ForkJoin`)(`/starwars/people/:id`) - hystrix - 如何對 API 使用熔斷模式的示例 (`/hystrix`) - scraper - 如何使用 scrape-it 從網站上抓取資料的示例 (`/scraper`) - 使用 swagger UI 以獲得示例 API 的完整列表 - metrics - 為所有 API 添加了基於 Prometheus 的指標 (`/metrics`) - API 部分 JSON 響應支援 ```bash curl http://localhost:3000/api/v1/starwars/people/1 ``` - Response ```bash { name: "Luke Skywalker", height: "172", mass: "77", hair_color: "blond", skin_color: "fair", eye_color: "blue", birth_year: "19BBY", gender: "male", homeworld: { name: "Tatooine", rotation_period: "23", orbital_period: "304", diameter: "10465", climate: "arid", gravity: "1 standard", terrain: "desert", surface_water: "1", population: "200000", residents: [ "http://swapi.co/api/people/1/", "http://swapi.co/api/people/2/", "http://swapi.co/api/people/4/", "http://swapi.co/api/people/6/", "http://swapi.co/api/people/7/", "http://swapi.co/api/people/8/", "http://swapi.co/api/people/9/", "http://swapi.co/api/people/11/", "http://swapi.co/api/people/43/", "http://swapi.co/api/people/62/" ], films: [ "http://swapi.co/api/films/5/", "http://swapi.co/api/films/4/", "http://swapi.co/api/films/6/", "http://swapi.co/api/films/3/", "http://swapi.co/api/films/1/" ], created: "2014-12-09T13:50:49.641000Z", edited: "2014-12-21T20:48:04.175778Z", url: "http://swapi.co/api/planets/1/" }, films: [ "http://swapi.co/api/films/2/", "http://swapi.co/api/films/6/", "http://swapi.co/api/films/3/", "http://swapi.co/api/films/1/", "http://swapi.co/api/films/7/" ], species: [ "http://swapi.co/api/species/1/" ], vehicles: [ "http://swapi.co/api/vehicles/14/", "http://swapi.co/api/vehicles/30/" ], starships: [ "http://swapi.co/api/starships/12/", "http://swapi.co/api/starships/22/" ], created: "2014-12-09T13:50:51.644000Z", edited: "2014-12-20T21:17:56.891000Z", url: "http://swapi.co/api/people/1/" } ``` --- ```bash curl http://localhost:3000/api/v1/starwars/people/1?data(name,gender,homeworld(gravity,population)) ``` - Response ```bash { "data": { "name": "Luke Skywalker", "gender": "male", "homeworld": { "gravity": "1 standard", "population": "200000" } } } ``` ### GraphQL - 已基於 [apollo framework](https://github.com/apollographql) 和參考實現添加了 `GraphQL` 支援(包括來自 `swapi.co` 的 `starwars api`) ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140149002-665777784.png) - 從 `http://localhost:3000/playground` 訪問 graphql playground - 從 `http://localhost:3000/graphiql` 訪問 graphiql tool - GraphQL API 跟蹤(可配置) - 用於快取和批處理的資料載入器(Dataloader) - 添加了多個**啟用 Dataloader** 示例 - RxJS API 呼叫 - peopleWithPlanets(id : ) - Starwars APIs - people(id: ) , planet(id: ) , starship(id: ) - peopleList(keys: [number]) - 查詢列表(請參閱 schema 詳細資訊以獲取完整列表) - quoteOfTheDay: String - random: Float - examples: [ExampleType] <-- JWT身份驗證 - example(id: Int): ExampleType - blog(id: Int) (Paginated query) - rollThreeDice: [Int] - peopleWithPlanet(id: Int): PeopleWithPlanetType (Uses RxJS to combine results from 2 APIs) - peopleDS(id: Int): PersonType (Based on REST DataSource) - people(id: Int): PersonType (Based on data loader) - planet(id: Int): PlanetType - starship(id: Int): StarshipType - peopleList(keys: [Int]): [PersonType] - movie: MovieType - 示例查詢執行 ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140203585-370005323.png) - Mutations - addExample(name: String!): ExampleType - addComment(comment: CommentInput!): Comment - login(email: String!,password: String!): UserType - 示例 Mutation 執行 ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140411017-892284278.png) - Subscriptions - exampleAdded (將檢查是否通過 mutation 添加了新元素) - commentAdded (每當通過 mutation 新增新 comment 時都會檢查) - 示例 Subscription 執行 ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140233371-1692599953.png) ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140259307-1897438208.png) - VSCode 除錯啟動配置(添加了預配置的除錯啟動器) - 在開發過程中添加了用於遙測的 Node 儀表板檢視 - 增加了 NodeJS 叢集模式(負載均衡 worker) - 啟動伺服器時,它會根據 CPU 數量新增 worker ```bash Master cluster setting up 4 workers... Worker 2828 is online Worker 2816 is online Worker 13956 is online Worker 3756 is online up and running in development @: LP-507B9DA1D355 on port: 3000 up and running in development @: LP-507B9DA1D355 on port: 3000 up and running in development @: LP-507B9DA1D355 on port: 3000 up and running in development @: LP-507B9DA1D355 on port: 3000 ``` ## Graphql 客戶端 API - 當我們構建基於 GraphQL 的伺服器時,可能需要從其他下游基於 GraphQL 的 API 伺服器獲取資料。 - 作為一個示例,`graphqlcool/graphql-request` 模組用於演示這一點,使用 `graphqlcool` 演示 graphQL api `https://api.graph.cool/simple/v1/movies`。 - API 規範 ```bash query { movie { releaseDate slug actors { name } } } ``` - API 輸出 ```json { "data": { "movie": { "releaseDate": "2010-08-28T20:00:00.000Z", "slug": "inception", "actors": [ { "name": "Leonardo DiCaprio" }, { "name": "Ellen Page" }, { "name": "Tom Hardy" }, { "name": "Joseph Gordon-Levitt" }, { "name": "Marion Cotillard" } ] } } } ``` ## 先決條件 安裝 npm 和 nodeJS npm version >= 3.x node version >= 6.x ## 安裝它 ```bash npm install ``` ## 設定 _外部環境_ - 編輯 **.{PROFILE}.env** 檔案 —— 其中的概要檔案(PROFILE)可以是測試(test)、開發(development)、生產(production) | 變數 | 描述 | 預設值 | | --------------------- | ------------------------------------------------------------------------------------------------------- | ------------- | | PORT | 伺服器埠 | 3000 | | LOG_LEVEL | 日誌級別 (info,debug,error) | info | | SESSION_SECRET | 用於簽名 cookie 的字串 | | | API_TIME_OUT | 預設API超時(以毫秒為單位) | 10000 | | TEST_TIME_OUT | 預設測試超時(以毫秒為單位) | 10000 | | JWT_AUTH | 啟用/禁用基於 JWT 的 API 安全 | true | | RSA_PRIVATE_KEY_FILE | RSA 私鑰路徑示例 | | | RSA_PUBLIC_KEY_FILE | RSA 公鑰路徑示例 | | | TOKEN_EXPIRY_TIME | JWT 令牌到期(從 /login 生成) | 1 hour (1h) | | STREAM_HYSTRIX | 啟用/禁用 Hystrix streaming 伺服器 (true 或 false) | false | | CORS | 在伺服器上啟用/禁用 CORS (true 或 false)。僅在生產版本中可用 | false | | CLUSTER_MODE | 在伺服器上啟用/禁用 Node Clustering (true 或 false) | false | | SWAGGER_API_DOCS_ROOT | 服務您的 Swagger API 檔案,以便它們可與 Swagger UI,PostMan 等前端工具一起使用。 | /api-docs/ | | GRAPHQL_SUBSCRIPTIONS | 啟用/禁用 GraphQL subscriptions (true 或 false) | true | | GRAPHQL_PLAYGROUND | 啟用/禁用 GraphQL Playground (true 或 false) | true | | GRAPHQL_TRACING | 啟用/禁用 GraphQL tracing (true 或 false) | true | | GRAPHQL_MOCK | 啟用/禁用 GraphQL Mock,對於未實現的介面(true 或 false) | true | | API_MOCK | 啟用/禁用 REST API Mock,對於未實現的路由(true 或 false) | true | ## 執行它 ### 執行在 _開發_ 模式 ```bash npm run dev ``` ### 執行在 _生產_ 模式 ```bash npm run compile npm start ``` #### 執行在 _VS Code 除錯_ 模式 ```bash npm run compile Press F5 ``` #### 執行帶有程式碼覆蓋率的測試 #### 執行單元測試 - 單元測試與要測試的模組或類位於同一目錄中 - 所有單元測試都需要有一個副檔名 `\*.spec.ts` ```bash npm run test ``` #### 執行整合測試 - 整合測試與要測試的模組或類位於同一目錄中 - 所有整合測試都需要有一個副檔名 \*.itest.ts - 首先構建整合測試。這將在構建中設定整合測試環境 ```bash npm run itest:build ``` - 執行 node 伺服器並對其進行整合測試 - 這等待伺服器啟動,執行測試,然後在完成時終止所有程序 ```bash npm itest:run ``` ### 嘗試一下 - 將您的瀏覽器指向 [http://localhost:3000](http://localhost:3000). - 直接或通過 swagger 呼叫示例 REST 端點 `http://localhost:3000/swagger` - 使用端點呼叫 Prometheus 指標 `curl http://localhost:3000/metrics` - 訪問針對 graphQL 的瀏覽器內建 IDE `http://localhost:3000/graphiql` - 訪問 graphQL playground app `http://localhost:3000/playground/` - 訪問健康檢查 api `curl http://localhost:3000/healthcheck` ### 檔案結構 ``` ├───public * nxplorer server 的登入頁面 ├───screenshots * 示例截圖 └───server * 伺服器配置和 API | ├───api * 伺服器上定義的 REST API | │ ├───controllers * 使用 RxJS,Inversify 的 API controller | │ │ ├───examples * Examples controller | │ │ ├───hystrix-demo * Hystrix demo controller | │ │ ├───security * JWT login API controller | │ │ ├───shop * 帶有產品,價格,庫存的示例商店 API | │ │ └───starwars * SWAPI controller | │ ├───interfaces * Service 介面 | │ ├───models * API 資料模型 | │ └───services * Service API 實現 | ├───common * Server 啟動和配置 | │ ├───config * Server 配置 | │ ├───constants * Inversify 和其他通用識別符號常量 | │ ├───interfaces * 公共 service 介面 | │ ├───middleware * 自定義中介軟體 | │ ├───models * 公共 API 資料模型 | │ ├───services * 公共 service 實現 | │ └───swagger * Swagger API 規範 (YAML) | | └───env.ts * DotENV 配置 | | └───server.ts * Express Server 啟動和配置 | └───graphql * 伺服器上定義的 GraphQL API | | ├───dataloader * GraphQL 資料載入器功能 | | ├───errors * GraphQL 錯誤處理程式 | | ├───schema * GraphQL Schema 型別 | | ├───mocks * GraphQL Mock Resolvers | | └───resolvers * GraphQL resolvers | | └───setupSchema.ts * GraphQL schema 配置 | └───index.ts * 主 Server 入口點 ├───helm * Helm chart 部署指令碼 │ ├───charts * │ └───templates * └───backpack.config.js * Backpack 配置 └───package.json * npm 依賴 └───build.js * ShellJS 實用程式構建指令碼 └───deploy-k8s.sh * Kubernetes 部署指令碼 └───Dockerfile * Docker 構建檔案 └───docker-compose.yml * Docker 構建和執行檔案 └───build-docker.bat|sh * Docker 構建檔案 └───itest.config.json * Jest 整合測試配置 └───unit.config.json * Jest 單元測試配置 └───tsconfig.json * typescript 配置 └───tslint.json * tslint 配置 └───.{profile}.env * 基於配置檔案的外部環境檔案 (development 開發,test 測試, production 生產) └───sonar-properties.json * sonarscanner|SonarQube 配置 └───jwtRS256.key|.key.pub * 伺服器使用的 JWT 私鑰和公鑰示例 ``` ### 日誌與 UUID - 添加了 UUID 傳播的示例實現。這取決於在請求物件中設定的 cookie 'UUID'。[LogService](server/common/services/log.service.ts) 將把 uuid 新增到它生成的所有日誌中。 - 例如,如果 'UUID' 設定為 `xxxx-dddd-ssss-wwww-ssss`,那麼呼叫 `/shop/products` API 將生成 ```json { "pid": 13492, "hostname": "LP-507B9DA1D355", "level": 30, "time": 1515859200496, "uuid": "xxxx-dddd-ssss-wwww-ssss", "fullUrl": "http://localhost:3000/api/v1/shop/products", "statusCode": 200, "responseTime": "1.187", "v": 1 } ``` ### GraphQL Mocks - 作為 TDD 的一部分,我們可能需要模擬 graphql 響應,直到我們能夠實現解析器為止 - 該基礎結構設定為僅為當前未實現的解析器新增模擬。因此,一旦實現可用,實際的解析器就會接手。 同樣,如果解析器執行失敗,那麼這將落在模擬響應上。 此功能只能在開發期間使用,因此已新增檢查以禁用“生產”版本中的此功能。 - 為了支援 - 設定環境變數 GRAPHQL_MOCK 為 true - 在 [mocks/index.ts](server/graphql/mocks/index.ts) 檔案定義 mock 解析器(resolver) - 作為示例,有查詢添加了 examplesMock, peopleMock - 示例輸出如下 ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140504434-482278881.png) ### RestAPI Mocks - 在 `..env` 檔案中啟用 `API_MOCK=true`。注意:為了安全起見,即使 `API_MOCK` 設定為 `true`,也無法在生產模式下使用 - 針對[自動產生 mock](https://github.com/BigstickCarpet/swagger-express-middleware/blob/master/docs/middleware/mock.md),`swagger-express-middleware` 模組提供了開箱即用的支援 - 步驟 - 在 [Api.yaml 檔案](server/common/swagger/Api.yaml) 定義 API swagger 規範 - 如果 express 路由中沒有可用的實現,那麼中介軟體將為這些 api 建立模擬 - 訪問 `nXplorer` (`/swagger`) 提供的 `swagger ui`,並引用標記為 `Mock API` 和帶有字首 `/mock` 的 API。該示例有兩個主要實體 —— `cars` 和 `drivers`。您可以搜尋、執行 CRUD 操作以及上傳和下載影象。 #### 構建 Docker 映象 ```bash ./build-docker.sh ``` #### k8s 部署 - 基於 Helm chart 的部署 ```bash ./deploy-k8s.sh ``` - 一個示例的輸出 ```bash release "nxplorerjs-microservice" deleted NAME: nxplorerjs-microservice LAST DEPLOYED: Fri Sep 22 22:10:58 2017 NAMESPACE: default STATUS: DEPLOYED RESOURCES: ==> v1/ConfigMap NAME DATA AGE nxplorerjs-microservice-starter 5 1s ==> v1/Service NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE nxplorerjs-microservice-starter 10.0.0.196 80:30316/TCP 1s ==> v1beta1/Deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nxplorerjs-microservice-starter 1 1 1 0 1s 注意: 1. 通過執行以下命令獲取應用程式 URL: export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services nxplorerjs-microservice-nxplorerjs-microservice-starter) export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT Express 微服務被部署到 http://192.168.99.100:30316/ ``` #### 使用 node 儀表板檢視(僅針對開發環境) - 去使用 node 儀表板檢視 ```bash npm run compile npm run dash ``` - 這將啟動帶有附加的 node 儀表板的應用程式,該儀表板提供有關記憶體,cpu 和日誌的詳細資訊 #### 安全 - 已使用[示例 JWT 私鑰和公鑰](https://gist.github.com/ygotthilf/baa58da5c3dd1f69fae9)實現了基於 JWT 的安全性 - REST API 和 GraphQL 都添加了示例實現。 - 驗證中介軟體程式碼可以在[這裡](server/common/middleware/auth-middleware.ts)檢視 #### JWT Security GraphQL - 基於 `JWT` 的安全性的演示實現已啟用一個查詢“示例”。 下面是測試的步驟。 - 如果啟用了 `JWT` 安全性(環境變數 `JWT_AUTH` 為 `true`),我們需要使用登入突變 `API` 來獲取示例 `JWT` 令牌(當前設定為1小時到期) - Step 1 - 使用登入 mutation(突變)來獲取有效使用者的 jwt 令牌。出於演示目的,可以提供任何電子郵件和密碼字串。該角色是可選的。如果未提供,則預設為角色 “USER” ``` mutation { login(email: "[email protected]", password:"admin",role:"ADMIN") { id role email jwt } } ``` ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140528901-1157200765.png) - Step 2 - 驗證“示例”是否無需身份驗證即可工作。 它將給出一個錯誤(注意:錯誤處理需要改進,但是這裡我們只看這個概念) ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140540575-1968663245.png) - Step 3 - 在執行 “examples” 查詢之前,使用 `Bearer token` 設定授權頭。 ``` { "Authorization": "Bearer xxx.xxx.xxx" } ``` ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140551844-1248187659.png) #### JWT Security REST APIs - 如果啟用了 JWT 安全性,那麼我們需要使用 `/login` API 獲取示例 JWT 令牌(當前設定為1小時到期) ```bash curl -X POST "http://localhost:3000/api/v1/login" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"email\": \"[email protected]\", \"password\": \"pwd\", \"role\": \"admin\"}" ``` - 示例輸出。注意,JWT token 是屬性 **idToken** 的值 ```json { "idToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJpYXQiOjE1MTQ4NjQ3ODMsImV4cCI6MTUxNDg2ODM4Mywic3ViIjoidGVzdEBnbWFpbC5jb20ifQ.hAEa6AL1Kxxxxxxx", "expiresIn": "1h" } ``` - `api/v1/examples` API,一個有效的 JWT 令牌必須在 “Authorization” header 中,在所有查詢中傳遞。注意,這只是一個示例。您也可以類似地輕鬆啟用新增端點。 - 請檢視 [examples controller](server/api/controllers/examples/controller.ts) ```typescript @controller('/examples', authMiddleware({ role: 'admin'})) ``` - 函式 [authMiddleware](server/common/middleware/auth-middleware.ts) 負責驗證在 header 中傳遞的 JWT token。 - 它可以擴充套件為也支援基於角色的訪問,併為此提供了支援。 - 注意:作為演示示例,這裡提供了公鑰和私鑰。理想情況下,在真實場景中從外部維護這些JWKS (JSON Web Key Set)端點 - “Authorization” header 中必須使用以下語法: Bearer xxxxxx.yyyyyyy.zzzzzz - 使用 [swagger ui](http://localhost:3000/swagger) 測試 - 點選 “Authorize” 按鈕,設定上面提到的 Bearer token - 現在所有 `/examples` 相關的 `api` 都可以工作了 ##### RBAC 測試 - 如果啟用了 JWT 安全性,並且我們使用 `/login` API 獲取示例 `JWT token`,但其角色為 “guest” 而不是 “admin” ```bash curl -X POST "http://localhost:3000/api/v1/login" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"email\": \"[email protected]\", \"password\": \"pwd\", \"role\": \"guest\"}" ``` - 在 “Authorization” header 中設定 Bearer token,用於後續呼叫任何 `/examples api`,將導致基於角色的授權失敗 ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140618406-391491549.png) ### GraphQL Directives - 這項工作正在進行中。 - 當前添加了一個使用 `@date` 指令的示例(如 `graphql-tools` 文件中所述) - Query (`{ today(format: "mmm-dd-yy") }`) - 這裡的格式基於@date scheme Directive,該格式接受解析器的輸出並格式化日期,然後再將其傳送給客戶端。 - Query ( `{ examplesWithAuth { id name } }` - 這是對 [JWT GraphQL APIs](#jwt-security-graphql) 部分中提到的示例查詢的一種變體。此處的區別在於,我們使用 `@auth` 指令根據角色來處理身份驗證,而不是對解析程式中的實現進行硬編碼。這是更清蒸的方法,並且與解析器分離。 - 查詢 schema `examplesWithAuth: [ExampleType] @auth(requires: ADMIN)` 使用 `@auth` 指令,該指令將攔截具有適當角色的經過身份驗證的使用者的呼叫檢查。(注意:您需要在之前執行 `login` mutation,然後使用 Authorization token 設定 HTTP header) ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140639842-1580551103.png) #### CSRF Security - 在**生產模式**中啟用了 CSRF 安全性 - 所有 POST API 都需要讀取瀏覽器中設定的 cookie “XSRF-TOKEN”,然後使用以下任一 `key` 將其傳遞到響應頭中 - req.headers['csrf-token'] - CSRF-Token HTTP 請求頭。 - req.headers['xsrf-token'] - XSRF-Token HTTP 請求頭。 - req.headers['x-csrf-token'] - X-CSRF-Token HTTP 請求頭。 - req.headers['x-xsrf-token'] - X-XSRF-Token HTTP 請求頭。 #### Compression - 預設情況下,壓縮是在伺服器上啟用的,並且基於[壓縮模組](https://www.npmjs.com/package/compression) - 配置詳細資訊位於 [compression.ts 檔案](server/common/config/compression.ts)中 - 如果需要在不壓縮的情況下獲取響應,請在請求頭中傳遞 `x-no-compression` 鍵 #### Hystrix 熔斷器支援 - 熔斷器支援已新增到專案中,並使用 hystrix 相容模組 [brakes](https://github.com/awolden/brakes) - Hystrix 預設禁用流支援。 - 通過在 [.env](.env) 檔案中將 STREAM_HYSTRIX 屬性設定為 “STREAM_HYSTRIX=true” 來啟用它 - 為了方便起見,提供了 Hystrix 伺服器的 Docker 版本,並在 [docker-compose.yml](docker-compose.yml) 檔案中進行了設定 ##### 在 Docker 上執行的步驟 ```bash npm run compile docker-compose build docker-compose up ``` - 在 Docker 上設定 3000 和 8080 的埠轉發 ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140711294-728020782.png) - Access the Hystrix dashboard at localhost:8080/hystrix and set the stream location to `localhost:3001/hystrix.stream` - 訪問 `localhost:8080/hystrix` 上的 Hystrix 儀表板,並將流位置設定為 `localhost:3001/hystrix.stream` - 在 `/api/v1/hystrix` 下執行示例,並在儀表板上檢視 hystrix stream 結果 ![](https://img2020.cnblogs.com/blog/436453/202012/436453-20201209140723824-1704399582.png) ### 與 SonarQube 整合(保證持續的程式碼質量) 假設您已經安裝了 SonarQube 5.5.6 (LTS) - 使用 [Sonar Typescript 外掛](https://github.com/Pablissimo/SonarTsPlugin#installation)和 Generic Test Coverage 外掛設定 SonarQube - 全域性安裝 sonar-scanner (`npm install --global sonar-scanner`) - 更新屬性 `sonar.host.url` 的 [`sonar-project.properties`](sonar-project.properties) 檔案以指向您的 SonarQube 伺服器。預設情況下,這假設 SonarQube 伺服器使用預設埠在本地執行 - 執行單元測試 ```bash npm run test ``` - 測試結果以 sonar 相容格式收集在結果資料夾中 - 將結果推送到 SonarQube ```bash npm run sonar-scanner ``` - 如果使用 SonarQube 6.x。它支援[通用測試資料](https://docs.sonarqube.org/display/SONAR/Generic+Test+Data) 修改 [package.json](package.json) 以設定適當的 sonarQube 版本 ```json jestSonar": { "reportPath": "reports", "reportFile": "test-reporter.xml", "indent": 4, "sonar56x": true } ``` 注意:對於 Sonar 6.x,將 sonar56x 設定為 “false”,這將生成使用 sonar 6 schema 的測試報告。 ### 負載測試 - [loadtest](https://www.npmjs.com/package/loadtest) 是用於負載測試的出色工具 - 使用步驟 - 將其安裝為全域性 npm 模組 ```bash npm install -g loadtest ``` - 啟動 nxplorerjs-microservice ```bash npm run start ``` - 針對生產版本執行負載測試。以下是一個示例 ```bash loadtest http://localhost:3000/api/v1/examples/1 -t 20 -c 20 ``` ## 互相交流 [一起雲原生](https://micro-ssr.hacker-linner.com/)