Note: This article was written with Fabric v1.4. Please refer to this article if you are testing it with Fabric v2.2.
Overview
Hyperledger Fabric comes with the Software Development Kit (SDK) for external access to a fabric network and chaincode functions deployed in the fabric network. Nowadays a more popular way of accessing an application is through REST API, which is not yet available officially in Hyperledger Fabric. Nevertheless it is not very difficult to implement one with some libraries.
This article is to show how to use ExpressJS to implement one. For those following me, you may notice that this has been shown in my previous article “Deep-Dive into Fabcar” (link). In this article, I run the API server in a separate instance. I see this as more realistic implementation.
Again I am using Fabcar as the application (chaincode) and First Network as the fabric network. The API server code is largely adopted from the original javascript code, with modification to meet the deployment.
Setup
In this setup we have two physical nodes
- Fabric Node: which runs the First Network with Fabcar chaincode instantiated
- API Server Node: which runs the API Server code and for external access
Here are the two nodes deployed as EC2 instances in AWS.
Setup the Fabric Node
Step 1: Make a fabric node by installing the prerequisite tools and all software related to Hyperledger Fabric. I am using release 1.4.3 in this demonstration. You can make reference to my previous article about building a fabric node.
Step 2: Run script fabcar/startFabric.sh
.
cd fabric-samples/fabcar
./startFabric.sh
After this script is fully executed, we now have a running First Network (2-org 4-peer setup), mychannel is up, Fabcar chaincode is installed on all four peer nodes, and instantiated on mychannel. The ledger has 10 car records, which is a result of initLedger() being invoked. Everything on the fabric network side is up and running.
To understand more about the First Network and Fabcar application, please take a look on my previous article, which has a more detail description.
Now let’s prepare an identity for API Server.
Step 3: Use client codes provided in fabcar/javascript
to create a user (user1) identity. This will be used in API Server.
cd javascript
npm install
node enrollAdmin.js
node registerUser.js
ls wallet/user1
Now we have all we need for API Server. They are
- Connection profile for org1:
first-network/connection-org1.json
- Node Package file:
fabcar/package.json
- User1 identity or wallet:
fabcar/javascript/wallet/user1/
These files will be copied to API Server later.
API Server: Design
We are using ExpressJS to build the API Server, and leveraging the client code query.js
and invoke.js
to build the actual logic. Here we discuss a bit the code and configuration setup.
API design
We are going to define four APIs. They are
GET /api/queryallcars
return all carsGET /api/query/CarID
return the car record of CarID specifiedPOST /api/addcar/
add a new car record with detail specified in bodyPUT /api/changeowner/CarID
change the car record with a new owner specified in body
apiserver.js
This is a standard ExpressJS code for an API Server. There are some modification from the provided query.js
and invoke.js
inside fabcar/javascript
.
- ccpPath is changed to the current directory as we are going to keep
connection-org1.json
in the same path. - in the gateway.connect of all APIs we change the option of discovery.asLocalhost to false
Connection Profile: connection-org1.json
The API Server relies on the connection profile given in order to make correct connection to fabric network. The file connection-org1.json
is directly obtained from the fabric network, i.e., first-network.
As we can cross-check the certificates are properly propagated when fabcar/startFabric.sh
is executed. Those for peer0.org1 and peer1.org1 are taken from the TLS root CA cert of org1, while the one for CA is the CA certificate of org1.
Note that all nodes are referred to localhost. We need to change it to the Fabric Node public IP later.
User Identity
We have generated a user (user1) on Fabric Node. They are stored inside wallet
directory. Inside which we see three files. They are the private key (signing key), public key and an object keeping the required information such as certificate.
We will copy this identity to API server later.
Setup the API Server Node
Step 1: Install npm, node and make on the API Server Node.
sudo apt-get update
sudo apt install curl
curl -sL https://deb.nodesource.com/setup_8.x | sudo bash -
sudo apt install -y nodejs
sudo apt-get install build-essentialnode -v
npm -v
Step 2: Create a directory in API server.
mkdir apiserver
cd apiserver
Step 3: Copy the following files from Fabric Node to the API server. We use a localhost to copy between the two EC2 instances.
# localhost (update your own IP of the two servers)# temp is an empty directory
cd tempscp -i ~/Downloads/aws.pem ubuntu@[Fabric-Node-IP]:/home/ubuntu/fabric-samples/first-network/connection-org1.json .scp -i ~/Downloads/aws.pem ubuntu@[Fabric-Node-IP]:/home/ubuntu/fabric-samples/fabcar/javascript/package.json .scp -r -i ~/Downloads/aws.pem ubuntu@[Fabric-Node-IP]:/home/ubuntu/fabric-samples/fabcar/javascript/wallet/user1/ .scp -r -i ~/Downloads/aws.pem * ubuntu@[API-Server-Node-IP]:/home/ubuntu/apiserver/
Step 4: We see all these files now in API Server. For sake of consistence we move user1/
to wallet/user1/
.
cd apiserver
mkdir wallet
mv user1 wallet/user1
Step 5: Now create the apiserver.js
file for the API server.
Step 6: Modify the connection profile connection-org1.json
with the Fabric Node IP address.
sed -i 's/localhost/[Fabric-Node-IP]/g' connection-org1.json
Step 7: Add entries in /etc/hosts
such that they will point to Fabric Node.
127.0.0.1 localhost
[Fabric-Node-IP] orderer.example.com
[Fabric-Node-IP] peer0.org1.example.com
[Fabric-Node-IP] peer1.org1.example.com
[Fabric-Node-IP] peer0.org2.example.com
[Fabric-Node-IP] peer1.org2.example.com
Step 8: Install the required modules.
npm install
npm install express body-parser --save
Step 9: Now we have all the required modules. We can run API server.
node apiserver.js
Access the API
Our API server listens to port 8080. We set up the API Server Node such that these APIs are accessible everywhere. In this demo I am using my localhost to access the APIs with curl.
Test 1: Query all cars
curl http://[API-Server-Node-IP]:8080/api/queryallcars
Test 2: Add a new car and query that car
curl -d '{"carid":"CAR12","make":"Honda","model":"Accord","colour":"black","owner":"Tom"}' -H "Content-Type: application/json" -X POST http://[API-Server-Node-IP]:8080/api/addcarcurl http://[API-Server-Node-IP]:8080/api/query/CAR12
Test 3: Change owner for a car and query that car again
curl http://[API-Server-Node-IP]:8080/api/query/CAR4curl -d '{"owner":"KC"}' -H "Content-Type: application/json" -X PUT http://[API-Server-Node-IP]:8080/api/changeowner/CAR4curl http://[API-Server-Node-IP]:8080/api/query/CAR4
And we get the same result using Postman.
Discussion
- Though we are using one Fabric Node to simulate a whole fabric network, this setup should be applicable to more complex deployment (e.g. multi-node, multi-channel). What we need is to provide the correct connection profile for API server to access.
- Note that the digital identity (certificate) is kept in API Server. This means that all transactions, no matter issued from anyone outside the world, are “proxied” by the API Server. If we need access control on the API Server, it is something beyond the Hyperledger Fabric. We can use traditional methods to control the access to API Server.
- If we wish to identify the incoming user in the fabric network level, we need to pass the user identity to end user, and develop a mechanism to let user sign the transaction. It is out of the scope of this article.
- This approach should be easily adapted into docker container, as far as all required components and configuration are done when creating an image. It can be further optimized with proper variable setup to avoid manual modification.
Summary
We have used a separate EC2 instance holding the API Server, accessing a fabric network with chaincode deployed. This serves a standard way to allow other applications or frontend accessing those chaincode functions according to the logic defined in the API server.