Backend for Frontend BFF Pattern Implementation

In its simple form it is like a proxy for other dependent APIs

How Does it help?

  • Don’t have to expose all the APIs externally, there by reducing the security concerns
  • Caching or rate limiting requests outside of APIs
  • Batch calls to other APIs and return the aggregated data.

Implementation

It can be easily implemented with NodeJs http-proxy-middleware

Install the following package

npm i http-proxy-middleware

Add the following file in your react application

app_server.js

const express = require('express');
const path = require('path');

const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

const api1Service = process.env.API1_ROUTE || 'api1:5555';
const api1Url = `http://${api1Service}`;
console.log(api1Url);

const api2Service = process.env.API2_ROUTE || 'api2:5555';
const api2Url = `http://${api2Service}`;
console.log(api2Url);


app.use(express.static(path.join(__dirname, 'build')));
app.use('/api1', createProxyMiddleware({ target: api1Url, changeOrigin: false}));
app.use('/api2', createProxyMiddleware({ target: api2Url, changeOrigin: false}));


app.get('/status', function(req, res) {
    res.sendStatus(200);
    console.log('working!');
});

app.get('*', function(req, res) {
    res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

let server = app.listen(3000);


Dockerfile

FROM node:10.19.0-jessie

EXPOSE 3000

ENV APP_ROOT=/root/app-root \
    NODEJS_VERSION=8 \
    NPM_RUN=start \
    NAME=nodejs

ENV HOME=${APP_ROOT} \
    NPM_CONFIG_PREFIX=${APP_ROOT}/.npm 


COPY . ${APP_ROOT}


WORKDIR ${APP_ROOT}

RUN npm install && npm run build

CMD node app_server.js

What Next ?

With GraphQL we can implement BFF very easily plus it has got lots of other benefits. If possible switch to GraphQL

Also See

Elegantly Consuming data In React Components Using Context API – Provider / Consumer Pattern


How do we make sure every component get the data they need ?

Pass data using properties

Consuming data using React Context API

Create Context

Context basically exposes state (Data) and the API which operates on the data to be consumed by Components and Context has to operate on a specific functional area covering set up components, for Example AuthContext deals with authentication, DataContext provides data for the application it can fine-grained level as well for example ToastContext provides a way to add Notification from anywhere in the application.

Refer this as an example

Provide Context

Providers are basically singletons, and are responsible for interaction with API and updating the state.

Step 1: Create Context

export const AuthContext = React.createContext();

Step 2: Populate Initial State

const initialState = {
  isAuthenticated: false,
  userId: "",
  roles: [],
  flash: ""
};

Step 3 : Expose data and API

constructor(props) {
    super(props);

    this.state = {
      ...initialState,
      doAuthRedirect: (idp, realm) => {
        return this.doAuthRedirect(idp, realm);
      },
      getAuthToken: (code, idp, realm) => {
        return this.getAuthToken(code, idp, realm);
      },
      reAuth: () => {
        return this.reAuth();
      },
      logout: () => {
        return this.logout();
      }
    };
  }

Consume Context Data

The following example shows how data can be consumed within a Component

export class Content extends Component {

  render() {
    
    return (<ToastConsumer>
      {({add}) => ( <div title="Notes" onclick= {() => this.copyToClipboard(add)}>
        <NotesIcon />
      </div>)}
    </ToastConsumer>
    )
  }
}

In the following snippet, App is is provider for AuthContext, DataContext and ToastProvider, and App is consumer for DataContext and AuthContext and not for ToastContext

import React from "react";

import "./App.css";
import { AuthContext, AuthProvider } from "../../providers/AuthProvider";
import { DataContext, DataProvider } from "../../providers/DataProvider";
import {ToastProvider} from 'react-toast-notifications';
import { Content } from "../Content/Content";
import {Timeout} from '../Timeout/Timeout'

function App() {
  return (
    <AuthProvider>
      <AuthContext.Consumer>
        {authContext => (
          <DataProvider authContext={authContext}>
            <DataContext.Consumer>
              {dataContext => (
                <ToastProvider>
                  <React.Fragment>
                    <div className="App">
                      <Content key={"app-1"} authContext={authContext} dataContext={dataContext}  />
                    </div>
                    <Timeout />
                  </React.Fragment>
                </ToastProvider>
              )}
            </DataContext.Consumer>
          </DataProvider>
        )}
      </AuthContext.Consumer>
    </AuthProvider>
  );
}
export default App;

Refer this as an example

References

SVG Sprites with React Applications

Preliminary

https://icomoon.io/app/#/select

Create SVG file sprites.svg under public folder, and paste the contents, under defs section one by one, each item would go as symbol

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
        <symbol id="icon-pacman" viewBox="0 0 32 32">
            <path d="M30.148 5.588c-2.934-3.42-7.288-5.588-12.148-5.588-8.837 0-16 7.163-16 16s7.163 16 16 16c4.86 0 9.213-2.167 12.148-5.588l-10.148-10.412 10.148-10.412zM22 3.769c1.232 0 2.231 0.999 2.231 2.231s-0.999 2.231-2.231 2.231-2.231-0.999-2.231-2.231c0-1.232 0.999-2.231 2.231-2.231z"></path>
        </symbol>
        <symbol id="icon-spades" viewBox="0 0 32 32">
            <path d="M25.549 10.88c-6.049-4.496-8.133-8.094-9.549-10.88v0c-0 0-0-0-0-0v0c-1.415 2.785-3.5 6.384-9.549 10.88-10.314 7.665-0.606 18.365 7.93 12.476-0.556 3.654-2.454 6.318-4.381 7.465v1.179h12.001v-1.179c-1.928-1.147-3.825-3.811-4.382-7.465 8.535 5.889 18.244-4.811 7.93-12.476z"></path>
        </symbol>
        <symbol id="icon-clubs" viewBox="0 0 32 32">
            <path d="M24.588 12.274c-1.845 0-3.503 0.769-4.683 2.022-0.5 0.531-1.368 1.16-2.306 1.713 0.441-1.683 1.834-3.803 2.801-4.733 1.239-1.193 2-2.87 2-4.734 0-3.59-2.859-6.503-6.4-6.541-3.541 0.038-6.4 2.951-6.4 6.541 0 1.865 0.761 3.542 2 4.734 0.967 0.93 2.36 3.050 2.801 4.733-0.939-0.553-1.806-1.182-2.306-1.713-1.18-1.253-2.838-2.022-4.683-2.022-3.575 0-6.471 2.927-6.471 6.541s2.897 6.542 6.471 6.542c1.845 0 3.503-0.792 4.683-2.045 0.525-0.558 1.451-1.254 2.447-1.832-0.094 4.615-2.298 8.005-4.541 9.341v1.179h12v-1.179c-2.244-1.335-4.448-4.726-4.541-9.341 0.995 0.578 1.922 1.274 2.447 1.832 1.18 1.253 2.838 2.045 4.683 2.045 3.575 0 6.471-2.928 6.471-6.542s-2.897-6.541-6.471-6.541z"></path>
        </symbol>
    </defs>
</svg>

Option #1 : One Back-end Call

Add to your SVG Component

 <svg viewBox="0 0 28.3 28.3" className="icon icon-pacman">
            <use xlinkHref="/sprites.svg#icon-pacman" />
            <span class="name"> icon-pacman</span>
          </svg>

Option #2 : Ehanced Previous approach

Lets create Icons.svg file as follows under src folder

Lets create Icon.js file as follows

import React from "react";
import Icons from "./Icons.svg";
import PropTypes from 'prop-types';

const Icon = ({ name, color, size }) => (
    <svg className={`icon icon-${name}`} fill={color} width={size} height={size} >
      <use xlinkHref={`${Icons}#icon-${name}`} />
    </svg>
  );

  Icon.propTypes = {
    name: PropTypes.string.isRequired,
    color: PropTypes.string,
    size: PropTypes.number
  };
  
  export default Icon;

Usage

import React from "react";
import "./App.css";
import Icon from "./Icon.js";

function App() {
  return (
    <div className="App">
      <div></div>
      <header className="App-header">
        <div>
          <Icon name="pacman" color="#FFFFFF" size={35} />
        </div>
        <div>
          <Icon name="spades" color="#FFFFFF" size={35} />
        </div>
        <div>
          <Icon name="clubs" color="#FFFFFF" size={35} />
        </div>
      </header>
    </div>
  );
}

export default App;

Option #3 : Zero Back-End Call

TODO

Multi IDP Support on React App For SSO Using OAuth2 / JWT

Okta Setup

Fusion Auth Setup

User

Add use to groups

Keycloak Setup

Refer this on setting up keycloak

Application Setup

Application setup

Demo

Source Code

Download from github

Source Code

Authentication / SSO with OAuth2 and JWT In React Application With NodeJs Back-end And KeyCloak IAM

We will use Keycloak as IDP, and OAuth 2 with JWT as AuthToken in react application with NodeJS (Express) back-end

KeyCloak IAM

Keycloak is a great tool for IAM from JBOSS, it is easy to get started and configure. Start KeyCloak as follows.

E:\softwares\keycloak-8.0.1\bin>standalone.bat

Add initial console user

E:\softwares\keycloak-8.0.1\bin>add-user.bat -u admin admin
Updated user 'admin' to file 'E:\softwares\keycloak-8.0.1\standalone\configuration\mgmt-users.properties'
Updated user 'admin' to file 'E:\softwares\keycloak-8.0.1\domain\configuration\mgmt-users.properties'
Press any key to continue . . .

Login with the credentials, created above

Start keycloak application

Create initial keycloak user

Login with initial keycloak user

Create New Realm

Create new client called react

Access Type should be confidential

Click Save, and go to Roles

New Role demo-user

New Role demo-admin

Client Secret can be found as follows

Add new user mnadeem

Add roles to user

OpenId Configuration

http://127.0.0.1:8080/auth/realms/demo/.well-known/openid-configuration

React App

Lets create react application following this

E:\practices\node>create-react-app react-sso-app
E:\practices\node>code react-sso-app

Create Backend NodeJs API

Lets follow the steps as described in here

E:\practices\node\react-sso-app>mkdir api
E:\practices\node\react-sso-app>cd api
E:\practices\node\react-sso-app\api>npm init --yes

Install dependencies

E:\practices\node\react-sso-app\api> npm install –save-dev babel-cli babel-preset-env nodemon
E:\practices\node\react-sso-app\api>npm install --save express
E:\practices\node\react-sso-app\api>npm install --save-dev rimraf
E:\practices\node\react-sso-app\api>npm install npm-run-all --save-dev

Final package.json of api project under react-sso-app

{
  "name": "api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "clean": "rimraf ./dist/",
    "build": "babel ./src/ --presets=babel-preset-env --out-dir dist --ignore ./node_modules,./.babelrc,./package.json,./npm-debug.log --copy-files",
    "server:dev": "nodemon ./src/server.js --exec babel-node --presets babel-preset-env",
    "server:prod": "node ./dist/server.js",
    "prod": "npm-run-all clean build server:prod",
    "dev": "npm-run-all server:dev"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-preset-env": "^1.7.0",
    "nodemon": "^2.0.2",
    "npm-run-all": "^4.1.5",
    "rimraf": "^3.0.1"
  },
  "dependencies": {
    "express": "^4.17.1"
  }
}

Add api to workspace

Create files, .babelrc, server.js

E:\practices\node\react-sso-app\api>npm run dev

> [email protected] dev E:\practices\node\react-sso-app\api
> npm-run-all server:dev


> [email protected] server:dev E:\practices\node\react-sso-app\api
> nodemon ./src/server.js --exec babel-node --presets babel-preset-env

[nodemon] 2.0.2
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `babel-node ./src/server.js --presets babel-preset-env`
App is listening for requests on port 5555

Lets install the following package

E:\practices\node\react-sso-app\api>npm install --save dotenv

lets create .env file (for local use) in api project

You can get all the details from keycloak (http://127.0.0.1:8080/auth/realms/demo/.well-known/openid-configuration)

SSO_CLIENT_ID=react
SSO_CLIENT_SECRET=202f6844-8b88-45b8-898a-327a74c10ab1
SSO_AUTH_URL=http://127.0.0.1:8080/auth/realms/demo/protocol/openid-connect/auth
SSO_TOKEN_URL=http://127.0.0.1:8080/auth/realms/demo/protocol/openid-connect/token
SSO_SCOPE=openid profile User roles
SSO_REDIRECT_URI=http://localhost:3000

TOKEN_SECRET=2423sdfsfsd3432fdwrerwtg

Lets add the following dependency

E:\practices\node\react-sso-app\api>npm install --save request-promise
E:\practices\node\react-sso-app\api>npm install --save jsonwebtoken

For more details look into the api project and react project

Lets start the api project

E:\practices\node\react-sso-app\api>npm run dev

Lets start the react project

e:\practices\node\react-sso-app>npm start

Demo

Make sure keycloak is running.

Key Points

There are three basic things:

  • Redirecting to IDP for authorization code
  • Get the JWT as auth token
  • Re authenticate using JWT until token is valid.

Source Code

Getting Started With ReactJs Using Visual Studio Code

Install Visual Studio Code

Download and Install Visual Studio Code.

Add Extensions

Install all the following extensions.

Create Initial React App

Install create-react-app npm generator, one time only.

C:\Users\nadeem\AppData\Roaming\npm\create-react-app -> C:\Users\nadeem\AppData\Roaming\npm\node_modules\create-react-app\index.js
+ [email protected]
updated 2 packages in 5.229s

C:\Users\nadeem>

Make sure %APP_DATA%\Roaming\npm is under PATH

E:\practices\react>create-react-app
Please specify the project directory:
  create-react-app <project-directory>

For example:
  create-react-app my-react-app

Run create-react-app --help to see all options.

This will take a while

E:\practices\react>create-react-app first-react-app

Here is the output

Success! Created first-react-app at E:\practices\react\first-react-app
Inside that directory, you can run several commands:

  npm start
    Starts the development server.

  npm run build
    Bundles the app into static files for production.

  npm test
    Starts the test runner.

  npm run eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

  cd first-react-app
  npm start

Happy hacking!

Start the app

E:\practices\react>cd first-react-app
E:\practices\react\first-react-app>npm start

Following window is automatically opened.

Lets leave the react application running and open new terminal, and type the following to open Visual Studio Code.

E:\practices\react>code first-react-app

This would open the following

Congratulations you are all set for React Application development.

File NamePurpose
README.md A markdown file under the root of the project. Describes all you need to get started with this generated reach app
package.json
References to all the npm packages used by this project, definitions of scripts to manage the script itself.
.gitignore list which files/folder to ignore while commiting to git
public folder contains static part of the application
favicon.ico icon shown in the browsers address bar
manifest.json Configuration file containing metadata according to Progressive Web Apps (PWA) Critera.
index.html HTML Page which reach uses as template, providing a context to react (root), it is starting point of application.
npm_modules generated after first app use, contains npm packages used by project, you don’t need to directly manage these files
src Most important folder, All application files resides here. There are some initial basic basic files
–  index.js : Starting point for the application
– index.css : Base styles for the application
– App.js : Contains the definition of Sample component.
– App.css : Contains the app component styles   
– App.test.js : App Component basic test cases.  
– serviceworker.js : Code to register service worker, in order to allow offline behavior as per the PWA requirements.

Hello React

Install react-dom-factories

package.json is automatically updated

Modify the index.html as follows, and save it, the page is automatically refreshed.

Debugging React App in Visual Studio Code

Step 1: Installation

Make sure Debugger for Chrome Visual Studio Code extension is installed

Step 2: Set Break Points

Just Click before line number

Step 3: Configuration

Configure the chrome debugger

Select chrome

This will add file launch.json under .vscode folder

In our case the application is running on port 3000, keep it running, and change the port in launch.json as follows

Step 4 : Debugging

Play with debugging controlls

React Developer Tools For Chrome

Install React Developer tool