Moment For Technology

BFF Microservice GraphQL/REST API layer development approach

Posted on Aug. 6, 2022, 1:50 a.m. by Logan Thompson
Category: The front end Tag: node.js

Cloud Native Node JS Express Reactive Microservices template (REST/GraphQL) This project provides complete Node JS/Typescript microservices template. Includes all the functionality required for production deployment, monitoring, debugging, logging, security, AND CI/CD. Examples of responsibility-based extensions have also been added to demonstrate how they can be used to build a microservice API edge service, a front-end back end (BFF), or as a basis for building any type of microservice.

Project address: nxplorerjs-Microservice-starter

Design principles

  • Use best-in-class modules to create production-ready microservices frameworks
  • Based on the 12-Factor APP principle
  • There is no custom code or wrapper, so any developer can modify/replace any module or implementation
  • Design patterns that can be implemented as a reference
  • Modular, replaceable and plug and play code
  • Provides an introduction to business API and microservice platform development
  • DevOps prepares code quality, unit and integration tests, and automated deployment.


  • Node JS.Express.Typescript, Dependency Injection Base
  • Based on theBackpack(Webpack) - Build, develop, package
  • To enable theSwagger- Express Swagger middleware/Swagger UI integration
  • GraphQL based onApollo Server 2.0withJWTSecurity, data loader (data loader) andRESTSample data source
  • throughgraphql-importsupportGraphQL SDL
  • During developmentGraphQL mock resolvers(Optional) -graphql-tools
  • Graphql-based client wrapping API -graphql-request
  • REST APIs - UseInversify Controller
  • Externalize configuration -DotEnv(Set, env-specific API URL)
  • Test -Jest , SuperTest , GraphQL Tester. Automated unit and integration testing infrastructure
  • Code coverage -Istanbul
  • Code quality -tslint
  • Container support -Docker , KubernetesThe cluster
  • Based on theHelm ChartDeployment support for
  • Prometheusintegration
  • API response logging, Express Server logging, UUID propagation -Pino
  • Reactive Extensions support -RxJS6
  • CORS, JSONObject restrictions, Helmet, CSRF-Express security
  • API based on IOC/dependency injection/Express annotations -Inversify
  • The document -TypeDocs
  • API exception handling utility
  • Standard HTTP Code for a cleaner Code
  • Sample API, pattern for reference
  • Sonar Qubeintegration
  • HystrixFuse support (with Brakes)
  • Jwt-based API security
  • Now use super fastpinoLogger to meet all logging needs
  • Built-in additional performance time logging

Check out REST API /examples/{id}

  "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
Copy the code


  • The API specification can be downloaded using the following URL
Copy the code
Copy the code
  • Examples - Basic examples of searching by ID examples in API (/examples/:id)
  • Shop - How to use Reactive Extensions for API choreography (FlatMap) example (/shop/priceByOptionId/:id)
  • Starwars - Examples of API choreography using Reactive Extensions (ForkJoin) (/starwars/people/:id)
  • Hystrix - Examples of how to use fuse mode with apis (/hystrix)
  • Scraper - example of how to scrape data from a web site using message-it (/scraper)
  • Use the Swagger UI to get a complete list of sample apis
  • Metrics - Added Promethe-based metrics for all apis (/metrics)
  • API partial JSON response support
    curl http://localhost:3000/api/v1/starwars/people/1
Copy the code
  • Response
        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: [
        films: [
        created: "The 2014-12-09 T13:50:49. 641000 z",
        edited: "The 2014-12-21 T20:48:04. 175778 z",
        url: ""
        films: [
        species: [
        vehicles: [
        starships: [
        created: "The 2014-12-09 T13:50:51. 644000 z",
        edited: "The 2014-12-20 T21: inquire. 891000 z",
        url: ""
Copy the code

curl http://localhost:3000/api/v1/starwars/people/1? data(name,gender,homeworld(gravity,population))Copy the code
  • Response
      "data": {
        "name": "Luke Skywalker"."gender": "male"."homeworld": {
          "gravity": "1 standard"."population": "200000"}}}Copy the code


  • Has been based onapollo frameworkAnd reference implementations are addedGraphQLSupport (including fromswapi.costarwars api)

  • From http://localhost:3000/playground access graphql playground

  • From http://localhost:3000/graphiql access graphiql tool

  • GraphQL API Trace (configurable)

  • Dataloader for caching and batch processing

  • Several examples of enabling Dataloader were added

    • RxJS API 调用 - peopleWithPlanets(id : )
    • Starwars APIs - people(id: ) , planet(id: ) , starship(id: ) - peopleList(keys: [number])
  • List of queries (see Schema details for a complete list)

    • quoteOfTheDay: String
    • random: Float
    • Examples: [ExampleType] -- JWT authentication
    • 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
    • Sample query execution

  • Mutations

    • addExample(name: String!) : ExampleType
    • addComment(comment: CommentInput!) : Comment
    • login(email: String! ,password: String!) : UserType
    • Sample Mutation execution

  • Subscriptions

    • ExampleAdded (will check if new elements have been added by mutation)
    • CommentAdded (checked whenever a new comment is added via mutation)
    • Sample Subscription execution

  • VSCode debug startup configuration (added preconfigured debug launcher)
  • A Node dashboard view for telemetry was added during development
  • - Added NodeJS cluster mode (load balancing worker)
    • When the server starts, it adds workers based on the number of cpus
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
Copy the code

Graphql client API

  • When we build graphQL-based servers, we may need to get data from other graphQL-based API servers downstream.

  • As an example, graphqlcool/graphql - request module are used to demonstrate this, use graphqlcool demo graphql API

  • API specification

query {
  movie {
    actors {
Copy the code
  • The API output
  "data": {
    "movie": {
      "releaseDate": "The 2010-08-28 T20:00:00) 000 z"."slug": "inception"."actors": [{"name": "Leonardo DiCaprio"
          "name": "Ellen Page"
          "name": "Tom Hardy"
          "name": "Joseph Gordon-Levitt"
          "name": "Marion Cotillard"}]}}}Copy the code

A prerequisite for

Install NPM and nodeJS

npm version = 3.x node version = 6.x

Install it

npm install
Copy the code

Set up theThe external environment

  • Edit.{PROFILE}. Env -- profiles can be test, development, production
variable describe The default value
PORT Server port 3000
LOG_LEVEL Log levels (Info, DEBUG,error) info
SESSION_SECRET A string used to sign cookies
API_TIME_OUT Default API timeout (in milliseconds) 10000
TEST_TIME_OUT Default test timeout (in milliseconds) 10000
JWT_AUTH Enable/disable JWT-BASED API security true
RSA_PRIVATE_KEY_FILE Example of an RSA private key path
RSA_PUBLIC_KEY_FILE Example of an RSA public key path
TOKEN_EXPIRY_TIME JWT token expiration (generated from /login) 1 hour (1h)
STREAM_HYSTRIX Enable/Disable Hystrix Streaming server (true or false) false
CORS Enable/disable CORS (true or False) on the server. Available only in production versions false
CLUSTER_MODE Enable/disable Node Clustering (true or false) on the server false
SWAGGER_API_DOCS_ROOT Service your Swagger API files so they can be used with Swagger UI, PostMan, and other front-end tools. /api-docs/
GRAPHQL_SUBSCRIPTIONS Enable/disable GraphQL Subscriptions (true or false) true
GRAPHQL_PLAYGROUND Enable/disable GraphQL Playground (true or false) true
GRAPHQL_TRACING Enable/Disable GraphQL Tracing (true or false) true
GRAPHQL_MOCK Enable/disable GraphQL Mock for unimplemented interfaces (true or false) true
API_MOCK Enable/disable REST API Mocks for unimplemented routes (true or false) true

Run it

Running on theThe development ofmodel

npm run dev
Copy the code

Running on theproductionmodel

npm run compile
npm start
Copy the code

Running on theVS Code debuggingmodel

npm run compile
Press F5
Copy the code

Run tests with code coverage

Running unit tests

  • Unit tests are in the same directory as the module or class to be tested
  • All unit tests need to have an extension\*.spec.ts
npm run test
Copy the code

Run integration tests

  • Integration tests are in the same directory as the module or class to be tested
  • All integration tests require an extension *.itest. Ts
  • Build the integration tests first. This sets up the integration test environment in the build
npm run itest:build
Copy the code
  • Run the Node server and test it for integration
  • This waits for the server to start, runs the tests, and then terminates all processes when finished
npm itest:run
Copy the code

Give it a try

  • Point your browser to http://localhost:3000.
  • Invoke the sample REST endpoint directly or through Swaggerhttp://localhost:3000/swagger
  • Call Prometheus metrics using endpointscurl http://localhost:3000/metrics
  • Access the built-in BROWSER IDE for graphQLhttp://localhost:3000/graphiql
  • Visit graphQL Playground apphttp://localhost:3000/playground/
  • Access the health check APIcurl http://localhost:3000/healthcheck

File structure

├ ─ ─ ─ public * nxplorer server login page ├ ─ ─ ─ screenshots * sample screenshots └ ─ ─ ─ server * server configuration and API | ├ ─ ─ ─ defined on the server API * REST API | │ ├── Controllers * Use RxJS Inversify API controller | │ │ ├ ─ ─ ─ examples * examples controller | │ │ ├ ─ ─ ─ hystrix - demo * hystrix demo controller | │ │ ├ ─ ─ ─ the security * JWT login API controller | │ │ ├ ─ ─ ─ shop * with the product, price, Inventory sample store API | │ │ └ ─ ─ ─ starwars * SWAPI controller | │ ├ ─ ─ ─ interfaces * Service interface | │ ├ ─ ─ ─ models * API data model | │ └ ─ ─ ─ Service API implementation services * | ├ ─ ─ ─ common * Server startup and configuration | │ ├ ─ ─ ─ the config * Server configuration | │ ├ ─ ─ ─ constants * Inversify And other common identifiers constants | │ ├ ─ ─ ─ interfaces * public service interface | │ ├ ─ ─ ─ middleware * custom middleware | │ ├ ─ ─ ─ models * | public API data model │ ├ ─ ─ ─ services * public service implementation | │ └ ─ ─ ─ swagger * swagger API specification (YAML) | | └ ─ ─ ─ env. Ts * DotENV configuration | | └ ─ ─ ─ for server ts * Express server Start and configure | └ ─ ─ ─ graphql * defined on the server graphql API | | ├ ─ ─ ─ dataloader * graphql data loader function | | ├ ─ ─ ─ errors * graphql error handler | | ├ ─ ─ ─ schema * GraphQL schema type | | ├ ─ ─ ─ away * GraphQL Mock Resolvers | | └ ─ ─ ─ Resolvers * GraphQL Resolvers | | └ ─ ─ ─ setupSchema. Ts * GraphQL schema configuration | └ ─ ─ ─ but ts * main Server entry point ├ ─ ─ ─ helm * helm chart deployment script │ ├ ─ ─ ─ charts * │ ├ ── garbage * ├ ──package.json * NPM relies on build.js * ShellJS utility to build scripts ├ ─ garbage, └─ docker-progenet, └─ docker-progenet, └─ docker-progenet, └─ docker-progenet └ ─ ─ ─ the build - docker. Bat | sh * docker build file └ ─ ─ ─ itest. Config. The json * Jest integration test configuration └ ─ ─ ─ unit.and config. Json * Jest unit test configuration └─ └─ garbage () -garbage (); └─ garbage () Testing, production production) └ ─ ─ ─ sonar - properties. Json * sonarscanner | SonarQube configuration └ ─ ─ ─ jwtRS256. Key |. The key. The pub * server USES the JWT private key and public key exampleCopy the code

Log and UUID

  • A sample implementation of UUID propagation was added. This depends on the cookie 'UUID' set in the request object. LogService adds uuid to all logs it generates.

  • For example, if 'UUID' is set to XXXX-DDDD-SssS-wwww-ssSS, then a call to the /shop/products API will generate

  "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
Copy the code

GraphQL Mocks

  • As part of TDD, we might need to emulate the GraphQL response until we can implement the parser
  • The infrastructure is set up to add emulation only for parsers that are not currently implemented. So, once the implementation is available, the actual parser takes over. Again, if the parser fails, this will fall on the mock response. This feature can only be used during development, so checks have been added to disable this feature in the "production" version.
  • In order to support
    • Set the environment variable GRAPHQL_MOCK to true
    • Defining a mock parser in mocks/index.ts file (resolver)
  • As an example, there are queries that add examplesMock, peopleMock
  • Sample output is as follows

RestAPI Mocks

  • in.Profile.envEnable in fileAPI_MOCK=true. Note: To be safe, even ifAPI_MOCKSet totrueAnd cannot be used in production mode
  • forAutomatic mock generation.swagger-express-middlewareModules provide out-of-the-box support
  • steps
    • Define the Api Swagger specification in api.yaml file
    • If there are no implementations available in express routing, the middleware creates mocks for these apis
  • accessnXplorer (/swagger) provided by theswagger uiAnd the reference is marked asMock APIAnd with a prefix/mockThe API. This example has two main entities --carsdrivers. You can search, perform CRUD operations, and upload and download images.

Build the Docker image

Copy the code

K8s deployment

  • Deployment based on Helm Chart
Copy the code
  • The output of an example
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  Nodes  80:30316/TCP 1S == V1beta1 /Deployment NAME DESIRED CURRENT Up-to-date AVAILABLE AGE NXplorerjs-microservice-starter 1 1 1 0 1s Note: 1. Get the application URL by running the following command: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_PORTExpress service is deployed to the code

Using the Node dashboard view (development environment only)

  • To use the Node dashboard view
npm run compile
npm run dash
Copy the code
  • This launches the application with an additional Node dashboard that provides details about memory, CPU, and logging


  • Jwt-based security has been implemented using the sample JWT private and public keys
  • Sample implementations have been added to both the REST API and GraphQL.
  • The verification middleware code can be viewed here

JWT Security GraphQL

  • Based on theJWTA demonstration implementation of security for the Here are the steps of the test.
  • If it's enabledJWTSecurity (environment variablesJWT_AUTHtrue), we need to use login mutationAPITo get an exampleJWTToken (currently set to expire in 1 hour)
  • Step 1 - Use login mutation to obtain the JWT token of a valid user. Any E-mail and password strings can be supplied for demonstration purposes. This role is optional. If not provided, the default role is USER.
mutation {
  login(email: "",
  password:"admin",role:"ADMIN") {
Copy the code

  • Step 2 - Verify whether the "example" works without authentication. It will give you an error (note: error handling needs to be improved, but we will only look at the concept here)

  • Step 3 - Before executing the "examples" query, use theBearer tokenSet the authorization header.
  "Authorization": "Bearer"
Copy the code

JWT Security REST APIs

  • If JWT security is enabled, then we need to use it/loginAPI gets sample JWT token (currently set to expire in 1 hour)
curl -X POST "http://localhost:3000/api/v1/login" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"email\": \"\", \"password\": \"pwd\", \"role\": \"admin\"}"
Copy the code
  • Sample output. Note that JWT token is the value of the attribute idToken
    "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJpYXQiOjE1MTQ4NjQ3ODMsImV4cCI6MTUxNDg2ODM4Mywic3ViIjoidGVzdE BnbWFpbC5jb20ifQ.hAEa6AL1Kxxxxxxx"."expiresIn": "1h"
Copy the code
  • api/v1/examplesAPI, a valid JWT token must be passed in the "Authorization" header in all queries. Note that this is just an example. You can similarly easily enable adding endpoints.
    • Please see theexamples controller
      @controller('/examples', authMiddleware(User{ role: 'admin'}))
      Copy the code
    • The authMiddleware function is responsible for validating JWT tokens passed in headers.
    • It can be extended to also support role-based access and has support for this.
    • Note: As a demonstration example, public and private keys are provided. Ideally, these JWKS (JSON Web Key Set) endpoints are maintained externally in a real-world scenario
  • The following syntax must be used in the Authorization header: Bearer XXXXX.yyyyy.zzzzzz
  • useswagger uitest
    • Click on the "Authorize" button to set up the Bearer token mentioned above
    • Now all/examplesThe relevantapiAll ready to work
RBAC test
  • If JWT security is enabled and we use/loginSample API acquisitionJWT token, but its role is "guest" rather than "admin"
curl -X POST "http://localhost:3000/api/v1/login" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"email\": \"\", \"password\": \"pwd\", \"role\": \"guest\"}"
Copy the code
  • Set up Bearer tokens in the Authorization header for subsequent calls to any/examples api, the role-based authorization fails

GraphQL Directives

  • The work is in progress.
  • A use is currently added@dateExamples of instructions (e.ggraphql-toolsAs described in the documentation)
  • Query ({ today(format: "mmm-dd-yy") }) - The format here is based on the @Date Scheme Directive, which takes the output from the parser and formats the date before sending it to the client.
  • Query ( { examplesWithAuth { id name } }- this is forJWT GraphQL APIsA variation of the sample query mentioned in the section. The difference here is that we use@authDirectives handle authentication by role rather than hard-coding the implementation in the parser. This is a more steamed approach and is separate from the parser.
  • Query schemaexamplesWithAuth: [ExampleType] @auth(requires: ADMIN)use@authDirective that intercepts invocation checks for authenticated users with appropriate roles. (Note: You need to run beforeloginMutation, then set the HTTP header using the Authorization token)

CSRF Security

  • CSRF security is enabled in production mode
  • All POST apis need to read the cookie "xSRF-Token" set in the browser and then use any of the followingkeyPass it to the response header
    • Req. Headers ['csrf-token'] - csrF-token Specifies the HTTP request header.
    • Req. Headers ['xsrf-token'] -xsrf-token Specifies the HTTP request header.
    • Req. Headers [' x-csrF-token '] -x-csrF-token Specifies the HTTP request header.
    • Req. headers['x-xsrf-token'] -x-xsrF-token Specifies the HTTP request header.


  • By default, compression is enabled on the server and is based on compression modules
  • Configuration details are in the compression. Ts file
  • If you need to get the response without compression, pass it in the request headerx-no-compression

Hystrix fuse support

  • Fuse support has been added to the project and brakes are used with the Hystrix compatible module
  • Hystrix disables streaming support by default.
  • Enable it by setting the STREAM_HYSTRIX property to "STREAM_HYSTRIX=true" in the.env file
  • For convenience, a Docker version of Hystrix server is provided and set up in the docker-comemage.yml file
Steps performed on Docker
npm run compile
docker-compose build
docker-compose up
Copy the code
  • Set port forwarding for 3000 and 8080 on Docker

  • Access the Hystrix dashboard at localhost:8080/hystrix and set the stream location to localhost:3001/
  • accesslocalhost:8080/hystrixOn the Hystrix dashboard and set the stream position tolocalhost:3001/
  • in/api/v1/hystrixExecute the sample below and view hystrix Stream results on the dashboard

Integration with SonarQube (for continued code quality)

Assuming you have SonarQube 5.5.6 (LTS) installed

  • Set up SonarQube using the Sonar Typescript plug-in and the Generic Test Coverage plug-in
  • Sonar - Scanner (npm install --global sonar-scanner)
  • Update the to point to your SonarQube server. By default, this assumes that the SonarQube server is running locally using the default port
  • Running unit tests
npm run test
Copy the code
  • The test results are collected in the results folder in a sonar compatible format
  • Push the results to SonarQube
npm run sonar-scanner
Copy the code
  • If you use SonarQube 6.x. It supports common test data

Modify package.json to set the appropriate sonarQube version

jestSonar": {"reportPath":"reports","reportFile":"test-reporter.xml","indent": 4,"sonar56x": true
Copy the code

Note: For Sonar 6.x, set Sonar56x to 'false' which will generate test reports using Sonar 6 Schema.

The load test

  • Loadtest is an excellent tool for load testing
  • Using the step
  • Install it as a global NPM module
npm install -g loadtest
Copy the code
  • Start the nxplorerjs - microservice
npm run start
Copy the code
  • Run load tests against production releases. Here is an example
loadtest http://localhost:3000/api/v1/examples/1 -t 20 -c 20
Copy the code
About (Moment For Technology) is a global community with thousands techies from across the global hang out!Passionate technologists, be it gadget freaks, tech enthusiasts, coders, technopreneurs, or CIOs, you would find them all here.