Build your first HCS powered web app
Mar 31, 2020
by Cooper Kunz
Developer Evangelist

In this tutorial, you will build your first Hedera Consensus Service (HCS) powered application. Specifically, I'll show you how to build a full-stack modern web application, which uses HCS as it’s message queue and a Hedera Mirror Node as a data store. It will use the Hedera JavaScript SDK to run on an Express.js server, which renders a website that is configured to a Socket.io chat client.

> Note: If you’re looking for a quick start, jump ahead and view the final codebase here.

Prerequisites

  1. Hedera Testnet Account
    1. Signup at portal.hedera.com
  1. Node version > v10
    1. Download at https://nodejs.org/en/download/
  1. NPM version > v6
    1. Download or update at https://docs.npmjs.com/try-the-latest-stable-version-of-npm

Background context

All messages in the chat application are submitted to Hedera where hashgraph will come to a consensus on a provable, fair order within the topic, with a sequence number, running hash, and consensus timestamp attached. Then the messages will be propagated through a Hedera Mirror Node before displaying to clients connected to the same topic.

Although transactions in this demo are signed by a server which acts as a “custodian” for the chat, the code is set up in an easily distributable way where users could host their own chats or run them locally, or implement additional types of key management. One example being the Composer Browser Extension, to further decentralize the application and remove any custodians.

Disclaimer: this is a simple demo app intended to demonstrate HCS as a pub-sub programming environment. Use it responsibly!

Download & set up the starter project

We’ve built a starter project for this application to make it easy for you to get started. This will basically come pre-populated with a non-HCS powered, centralized chat application. This way we can focus on new concepts, like decentralizing the application, rather than on things like the HTML or CSS that make the application look nicer. Let’s get started!

You can easily download the starter project from GitHub by running the following command:

Code Snippet Background

/* in your terminal */

git clone https://github.com/hashgraph/hedera-hcs-chat-js.git

This should download the GitHub repository to your local machine.

Now, change directories into the newly cloned GitHub repository.

Code Snippet Background

/* in your terminal */

cd hedera-hcs-chat-js

Within the correct directory, we will switch to a specific branch on our GitHub repository to get started.

Code Snippet Background

/* in your terminal */

git checkout no-hcs

Next, we can install the project’s dependencies via npm.

Code Snippet Background

/* in your terminal */

npm install

After you have successfully installed your npm dependencies, we’ll run our chat application.

Code Snippet Background

/* in your terminal */

node server.js

Check point: Assuming you're all set up so far, this should open the following window on your local machine.

Everything going okay so far? If not, feel free to file an issue, or contact us in Discord.

Hcs Chat Demo 0

If you type in a few messages & click the send icon, you should see them appear in your chat.

Hcs Chat Demo 1

You’ll notice that you didn’t configure, or install anything related to Hedera yet. As discussed above, this is just a regular old chat application. There’s not even a database to store messages, let alone a mechanism by which we could trust their authenticity!

Let’s fix this, by rearchitecting this app to send messages over HCS.

Start decentralizing your chat application with HCS

There are a few key parts to building on HCS that may be good to know. Here’s a bit of terminology.

  • Hedera Client - A client which has a valid Account ID, public & private key pairings, for a respective network. These clients send transactions to a Hedera network for consensus.
  • Hedera Node - A computer that reaches consensus on transactions within a given Hedera network.
  • Mirror node client - A client which subscribes to a Hedera Mirror node, in order to receive post-consensus messages.
  • Mirror node - A computer that receives post consensus transactions from other Hedera networks.
  • Topic - A namespace where messages can be published to. Topics can be permissioned, or permissionless, as defined upon their creation.
  • Message - A string of data submitted to a specific topic. Post-consensus, these messages will contain a cryptographically provable “running hash” which builds an unbreakable chain within a topic, among other details.

That may seem like a lot, but don’t worry! With the Hedera JavaScript SDK, we make it really easy to manage all of these pieces.

The first thing that you’re going to do is install the SDK at version 1.1.4.

Code Snippet Background

/* in your terminal */

npm i @hashgraph/[email protected]

Your package.json file should now look something like this, assuming everything was successful.

Code Snippet Background

/* package.json */

{

“name”: “chat”,

“version”: “1.0.0",

“description”: “”,

“main”: “index.js”,

“scripts”: {

“test”: “echo \“Error: no test specified\” && exit 1"

},

“author”: “Cooper Kunz”,

“license”: “ISC”,

“dependencies”: {

“dotenv”: “^8.2.0”,

“express”: “^4.17.1”,

“inquirer”: “^7.0.4”,

“open”: “^7.0.0”,

“socket.io”: “^2.3.0”,

“text-encoding”: “^0.7.0”

},

“repository”: {

“type”: “git”,

“url”: “git://github.com/Cooper-Kunz/hello-hedera-chat-js.git”

}

}

The first thing that we’re going to want to do with the JavaScript SDK, is configure our client connection. This will build, sign, and execute all of the transactions in this application. Import the `Client` module from the Hedera JavaScript SDK into your server.js file. After importing it, we'll instantiate a new Client for the public Testnet, and provide our Account's ID and the associated private key.

Note: if you don't have an Account on the public testnet, sign up at portal.hedera.com.

Code Snippet Background

/* within server.js */

/* ... other imports ... */


/* import Hedera JavaScript SDK modules */

const { Client } = require("@hashgraph/sdk");


/* create your Client connection to Hedera's public testnet */

const HederaClient = Client.forTestnet();

HederaClient.setOperator("YOUR-ACCOUNT-ID", "YOUR-PRIVATE-KEY");


/* ... rest of your code ... */

Congratulations! That's actually everything that you need to configure your Hedera Client.


Check point: to ensure that you have everything set up properly, and your Hedera JavaScript SDK is allowing you to set your Account as the operator, go ahead and run your chat application again. We're not necessarily doing anything with this Client yet, but if the app runs as before, you're good to continue with the tutorial!

Did you run into any issues during this check point? File an issue, or contact us in Discord.


Next, we're going to create your first Hedera Consensus Service topic, which will be the "channel" for messages in this app.

We will add `ConsensusTopicCreateTransaction` to the list of modules that is being imported from the JavaScript SDK alongside the client. After that, we build a new async function, which will create a transaction. This transaction will create our topic, and then wait until Hedera reaches consensus, to fetch the transaction's receipt. This receipt will contain our new topic ID if it was successful, otherwise hopefully some information about why the transaction failed.

Code Snippet Background

/* within server.js */


/* ... other imports ... */


/* import your new Hedera JavaScript SDK modules */

const { Client, ConsensusTopicCreateTransaction } = require("@hashgraph/sdk");


/* create your Client connection to Hedera's public testnet */

/* if you don't have an account & private key, signup at portal.hedera.com */

const HederaClient = Client.forTestnet();

HederaClient.setOperator("YOUR-ACCOUNT-ID", "YOUR-PRIVATE-KEY");


/* create new async function */

async function createNewTopic() {


/* create your first HCS topic transaction! */

const txId = await new ConsensusTopicCreateTransaction().execute(HederaClient);

await sleep(3000); /* wait for Hedera to reach consensus */

const receipt = await txId.getReceipt(HederaClient);

var newTopicId = receipt.getTopicId();

console.log(`Your new topic was created with an ID = ${newTopicId}`);

return newTopicId;

}


/* write a JavaScript function that allows our program to "sleep" */

/* this lets us wait for Hedera to reach consensus, before fetching the transaction receipt */

function sleep(ms) {

return new Promise(resolve => setTimeout(resolve, ms));

}


var newTopicId = "";


/* write a new async function that will generate our topic & handoff to run the app */

async function init() {

newTopicId = await createNewTopic();

await sleep(9000); /* wait for our new topic to reach a Mirror Node */

runChat(); /* handoff to runChat() here instead of at the end of the file */

}


/* ... rest of your code below */

Check point: Go ahead and run your chat application now. It should connect to Hedera's public testnet with your Client & account credentials, and then go on to generate a new HCS topic. However, you'll notice that the rest of the chat application is untouched. In the next step, we'll configure a Mirror Node subscription, and send our chat messages over HCS before displaying to our users.

Did you have any issues so far? File an issue, or contact us in Discord.


Next, we're going to start "publishing" our chat application's messages over this Hedera Consensus Service topic. Then finally, in the following step, we will "subscribe" to this topic from a Hedera Mirror Node.

Add the `ConsensusSubmitMessageTransaction` to the list of modules being imported from the Hedera JavaScript SDK. Then we will put this new transaction type to work within the runChat() function, which is powering our (currently) centralized chat.

Code Snippet Background

/* within server.js */


/* updated imports from the Hedera JavaScript SDK */

const { Client, ConsensusTopicCreateTransaction, ConsensusSubmitMessageTransaction } = require("@hashgraph/sdk");

Most of the interesting things in this application happen in the runChat() function. Let's update that now.

Code Snippet Background

/* ... within runChat() & server.js ... */


/* When a new chat message is sent to the Express.js Server */

client.on("chat message", function(msg) {

/* lets generate an HCS transaction, and send it to Hedera */

try {

new ConsensusSubmitMessageTransaction().setTopicId(newTopicId).setMessage(msg).execute(HederaClient);

console.log("ConsensusSubmitMessageTransaction", msg);

}

catch (error) {

console.log("ERROR: ConsensusSubmitMessageTransaction", error);

}

}); /* close client.on() */


/* ... the rest of runChat() & server.js ... */

That's great! Now whenever a user types in a new chat, it's being sent over the Hedera Consensus Service.


Check point: Go ahead and run your chat application now. It should now send messages over the Hedera Consensus Service when they are typed into the chat. Feel free to confirm this by looking at a network explorer like HashScan or DragonGlass, and search for your topic ID. Also, when visiting a network explorer, make sure that you're viewing the correct network (mainnet vs. testnet).

Did you have any issues so far? File an issue, or contact us in Discord.


When building with HCS, we need to wait until Hedera reaches consensus, and persists the message to a Hedera Mirror Node, before displaying to the rest of our clients. This will "close the loop" on our HCS publish-subscribe infrastructure! To do this, we're going to import `MirrorClient` to establish a connection to a Hedera Mirror Node. We're also going to import the `MirrorConsensusTopicQuery` module, which will allow us to query specific topics. We'll put these to work in our runChat() function, which is the final step in this tutorial. Update your code with the following:

Code Snippet Background

/* ... within server.js ... */


/* ... other imports ... */


/* import your new Hedera JavaScript SDK modules */

const { Client, ConsensusSubmitMessageTransaction, ConsensusTopicCreateTransaction, MirrorClient, MirrorConsensusTopicQuery } = require("@hashgraph/sdk");


/* create your Mirror node client connection */

const mirrorNodeAddress = new MirrorClient("hcs.testnet.mirrornode.hedera.com:5600");


/* ... other config & functions ... */


/* ... within runChat() ... */


/* subscribe to our Hedera Mirror node */

/* when the Mirror Node receives messages in our topic, send them to our clients */

const TextDecoder = require("text-encoding").TextDecoder;


try {

new MirrorConsensusTopicQuery().setTopicId(newTopicId).subscribe(mirrorNodeAddress, response => {

/* use a node.js package to decode our text responses */

var postHCSMessage = new TextDecoder("utf-8").decode(response["message"]);

/* send responses from our Hedera Mirror Node back to our Socket.io clients */

io.emit("chat message", postHCSMessage);

});

}

catch(error) {

console.log("ERROR: MirrorConsensusTopicQuery()", error);

}


/* ... rest of runChat() & server.js ... */

Final check point: Go ahead and run your chat application now.

It should connect to Hedera's public testnet with your Client & account credentials, and then go on to generate a new HCS topic. In this past step, we've also started sending our chat messages over HCS with a new `ConsensusSubmitMessageTransaction` and subscribed to a Mirror Node with a `MirrorConsensusTopicQuery`. After our messages are sent over HCS and received from a Mirror Node, we push these messages out to any Socket.io clients that are listening to our server. You should be able to run the application and have a working chat application, in which all messages are shared over the Hedera Test Network using HCS!


Congratulations! You've finished your first steps in taking a chat web app and decentralizing it on Hedera!

If you'd like, check out the master branch of the HCS-Chat demo GitHub repository. There are some more features included there for you to check out, including dynamic initialization, and improved data management from the Hedera Mirror Node, which gets displayed to our clients. It looks a bit more like this!

Hcs  Chat  Demo  Final

There are a lot of things not covered in this tutorial. For instance, decentralized (non-custodial) key management. As a "jumping off point", try implementing something like the Composer Browser Extension, for users to sign their transactions without sending them to a centralized Express.js server. You can also look at the Hedera HCS-SXC demo app for ways to integrate key rotation, message encryption, and more!


Did you have any issues with the tutorial? File an issue, or contact us in Discord.