1 min read

Hyperledger Fabric Fabcar Client App

This is the client web based application using React JS for FabCar Blockchain application which is a sample app from Hyperledger Fabric.

Screenshots

All cars

Fabcar Client

Add car

Fabcar Client

Change owner

Fabcar Client

Project Repository

Prerequisite

To start the application you need to do some steps such as:

  • Network setup
  • Enroll admin user and register user
  • Start API REST Server for client app
  • Start client app

Network setup

In this sample application, I used the first network from Hyperledger Fabric. To setup, just clone the project fabric-sample from github repository and do the following.

$ git clone https://github.com/hyperledger/fabric-samples.git
$ cd fabric-samples/fabcar
$ ./startFabric.sh

Enroll admin and register user

In the fabcar directory, go to javascript directory and run:

$ node enrollAdmin.js
$ node registerUser.js

After running this, we can test by querying some records from the network by running:

$ node query.js

If you can query cars without any errors, you can move to the next step.

Start API REST Server for client app

Also in the fabcar/javascript directory, create a file app.js in that directory and put this code. you get get this file from here as well.

const express = require('express')
const app = express()

const { FileSystemWallet, Gateway } = require('fabric-network');
const path = require('path');

const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json');

// CORS Origin
app.use(function (req, res, next) {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
  res.setHeader('Access-Control-Allow-Credentials', true);
  next();
});

app.use(express.json());

app.get('/cars', async (req, res) => {
  try {
    const walletPath = path.join(process.cwd(), 'wallet');
    const wallet = new FileSystemWallet(walletPath);
    const userExists = await wallet.exists('user1');
    if (!userExists) {
      res.json({status: false, error: {message: 'User not exist in the wallet'}});
      return;
    }

    const gateway = new Gateway();
    await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });
    const network = await gateway.getNetwork('mychannel');
    const contract = network.getContract('fabcar');
    const result = await contract.evaluateTransaction('queryAllCars');
    res.json({status: true, cars: JSON.parse(result.toString())});
  } catch (err) {
    res.json({status: false, error: err});
  }
});

app.get('/cars/:key', async (req, res) => {
  try {
    const walletPath = path.join(process.cwd(), 'wallet');
    const wallet = new FileSystemWallet(walletPath);
    const userExists = await wallet.exists('user1');
    if (!userExists) {
      res.json({status: false, error: {message: 'User not exist in the wallet'}});
      return;
    }

    const gateway = new Gateway();
    await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });
    const network = await gateway.getNetwork('mychannel');
    const contract = network.getContract('fabcar');
    const result = await contract.evaluateTransaction('queryCar', req.params.key);
    res.json({status: true, car: JSON.parse(result.toString())});
  } catch (err) {
    res.json({status: false, error: err});
  }
});

app.post('/cars', async (req, res) => {
  if ((typeof req.body.key === 'undefined' || req.body.key === '') ||
      (typeof req.body.make === 'undefined' || req.body.make === '') ||
      (typeof req.body.model === 'undefined' || req.body.model === '') ||
      (typeof req.body.color === 'undefined' || req.body.color === '') ||
      (typeof req.body.owner === 'undefined' || req.body.owner === '')) {
    res.json({status: false, error: {message: 'Missing body.'}});
    return;
  }

  try {
    const walletPath = path.join(process.cwd(), 'wallet');
    const wallet = new FileSystemWallet(walletPath);
    const userExists = await wallet.exists('user1');
    if (!userExists) {
      res.json({status: false, error: {message: 'User not exist in the wallet'}});
      return;
    }

    const gateway = new Gateway();
    await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });
    const network = await gateway.getNetwork('mychannel');
    const contract = network.getContract('fabcar');
    await contract.submitTransaction('createCar', req.body.key, req.body.make, req.body.model, req.body.color, req.body.owner);
    res.json({status: true, message: 'Transaction (create car) has been submitted.'})
  } catch (err) {
    res.json({status: false, error: err});
  }
});

app.put('/cars', async (req, res) => {
  if ((typeof req.body.key === 'undefined' || req.body.key === '') ||
      (typeof req.body.owner === 'undefined' || req.body.owner === '')) {
    res.json({status: false, error: {message: 'Missing body.'}});
    return;
  }

  try {
    const walletPath = path.join(process.cwd(), 'wallet');
    const wallet = new FileSystemWallet(walletPath);
    const userExists = await wallet.exists('user1');
    if (!userExists) {
      res.json({status: false, error: {message: 'User not exist in the wallet'}});
      return;
    }

    const gateway = new Gateway();
    await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });
    const network = await gateway.getNetwork('mychannel');
    const contract = network.getContract('fabcar');
    await contract.submitTransaction('changeCarOwner', req.body.key, req.body.owner);
    res.json({status: true, message: 'Transaction (change car owner) has been submitted.'})
  } catch (err) {
    res.json({status: false, error: err});
  }
});

app.listen(3000, () => {
  console.log('REST Server listening on port 3000');
});

Save the file and before running the REST server, you need to install 2 dependencies (express and fabric-network) by using:

$ npm install express fabric-network

Start up REST server

$ node app.js

Start client app

Windows

Clone the project repository and update environment variables in project folder

$ git clone https://github.com/chhaileng/fabcar-client.git
$ cd fabcar-client

Change localhost to Server IP

REACT_APP_API_HOST=localhost # Server IP

Install dependencies and build project

$ npm install create-react-app -g
$ npm install axios react-dom react-router-dom
$ npm run build

Or you can run the project in development server

$ npm start

Ubuntu or MacOS

Clone the project repository and update environment variables in project folder

$ git clone https://github.com/chhaileng/fabcar-client.git
$ cd fabcar-client

Change localhost to Server IP

REACT_APP_API_HOST=localhost # Server IP

Create new React App

$ cd ..
$ npm install create-react-app -g
$ create-react-app fabcar-ui

Remove default files created by React and copy source codes from fabcar-client directory to new React App directory

$ cd fabcar-ui
$ rm -rf src
$ cp -r ../fabcar-client/src ../fabcar-client/.env.* ./
$ cp ../fabcar-client/public/index.html ./public/

Install dependencies and build project

$ npm install axios react-dom react-router-dom
$ npm run build

Or you can run the project in development server

$ npm start

Deploy via docker for both Windows and Ubuntu (NGINX)

Go to your React App root directory and create default.conf file.

$ vi default.conf

Copy and paste below script to default.conf

server {
   listen 80;
   server_name  localhost;

   location / {
       root   /usr/share/nginx/html;
       index  index.html;
       try_files $uri $uri/ /index.html;
   }
}

Start Nginx container on port 8081 or any port you want to expose.

$ docker run --name fabcar-nginx -d -p 8081:80 -v $(pwd)/build:/usr/share/nginx/html -v $(pwd)/default.conf:/etc/nginx/conf.d/default.conf nginx

Happy hacking...

Fork, Start or clone this project: https://github.com/chhaileng/fabcar-client
SHARE THIS POST