7 mins

blockchain nestjs javascript substrate

Edit

Connecting to Substrate blockchain from NestJS

Create a sample NestJS project

First of all, we should create a NestJS project. Please use the nest-cli to generate the project template:

nest new substrate-nests
cd substrate-nests

This commands generate for us a sample project with one module and generate code for one GET API endpoint:

src
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
└── main.ts

After you successfully create the sample project you could run the dev server:

yarn start:dev

This command runs dev server and now you can see text Hello World! if you open http://localhost:3000/.

Connecting to substrate

Preparation

To connect to the substrate node we will be using the library polkadot-js. Please, add this library to dependency:

yarn add @polkadot/api

To work with this library please set flag esModuleInterop to true in tsconfig.json file and restart the dev server.

Now you can add necessary imports to app.service.ts file:

import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

The next thing we should set is the URL where our node exists. Please add these lines after the import section in app.service.ts file:

const SUBSTRATE_URL = 'wss://dev-node.substrate.dev:9944'; // dev node of substrate blockchain
// const SUBSTRATE_URL = 'ws://127.0.0.1:9944'; // if you have substrate install locally you can use this address

We are going to connect to the substrate node in onModuleInit lifecycle function. It allows us to connect to the node after module initialized.

@Injectable()
export class AppService implements OnModuleInit { // class should implement OnModuleInit interface
  private api: ApiPromise;

  // please, add this function to your class
  async onModuleInit() {
    Logger.log('Connecting to substrate chain...');
    const wsProvider = new WsProvider(SUBSTRATE_URL);
    this.api = await ApiPromise.create({ provider: wsProvider });
  }

After the restart you could see our message in logs:

[Nest] 6701   - 04/07/2020, 1:21:02 PM   [NestFactory] Starting Nest application...
[Nest] 6701   - 04/07/2020, 1:21:02 PM   [InstanceLoader] AppModule dependencies initialized +13ms
[Nest] 6701   - 04/07/2020, 1:21:02 PM   [RoutesResolver] AppController {/}: +5ms
[Nest] 6701   - 04/07/2020, 1:21:02 PM   [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 6701   - 04/07/2020, 1:21:02 PM   Connecting to substrate chain... +1ms

Getting data

For the test purposes, we will get the chain name, node name, version, the number of the latest block and the current timestamp. For this let’s create the new function in AppService class:

async getSimpleData() {
  await this.api.isReady;
}

The first line in the function getSimpleData waits until our connection to substrate node will be ready.

Now we can add code to get the data:

async getSimpleData() {
  await this.api.isReady;

  const chain = await this.api.rpc.system.chain();
  const nodeName = this.api.rpc.system.name();
  const nodeVersion = this.api.rpc.system.version();
  const header = this.api.rpc.chain.getHeader();
  const now = await this.api.query.timestamp.now();

  return {
    chain,
    nodeName,
    nodeVersion,
    headerNumber: header.number,
    now
  };
}

After we create this function we could change our endpoint’s code to respond with this data. Please update the function getHello in AppController with this code:

@Get()
async getHello(): Promise<string> {
  const data = await this.appService.getSimpleData();

  return `
    Chain: ${data.chain}<br/>
    Node name: ${data.nodeName}<br/>
    Node version: ${data.nodeVersion}<br/>
    Number of latest block: ${data.headerNumber}<br/>
    Now: ${new Date(Number(data.now.toString())).toISOString()}<br />
  `;
}

Here we just create a simple response with the collected data. After the server has successfully restarted you can open http://localhost:3000/ and see the result:

Result

The next thing that we can do is getting the balance of some account.

Please add to the function getSimpleData the following lines:

  const ADDR = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
  const now = await this.api.query.timestamp.now();

  // to get balance will be used the different method depends of substrate version
  const balance = await this.api.query.balances.freeBalance(ADDR);
  // or this
  // const { data } = await this.api.query.system.account(ADDR);
  // const balance = data.free;
  
  return {
    chain,
    nodeName,
    nodeVersion,
    headerNumber: header.number,
    now,
    balance,
  };
}

The address 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY is a predefined test account for testing purpose. If you are creating a substrate chain from the tutorial then you will have this account.

Depends on what version of substate node you are trying to connect the function account() can be not defined. In this case please use function freeBalance(). The function freeBalance is depreciated, for now, please read more about it in polkadot FAQ.

Also, we need to update AppController. Please add the line Balance: ${data.balance} to the returned template literal: The Balance is a Pallet defines a cryptocurrency for blockchain.

@Get()
async getHello(): Promise<string> {
  const data = await this.appService.getSimpleData();

  return `
    Chain: ${data.chain}<br/>
    Node name: ${data.nodeName}<br/>
    Node version: ${data.nodeVersion}<br/>
    Number of latest block: ${data.headerNumber}<br/>
    Now: ${new Date(Number(data.now.toString())).toISOString()}<br />
    Balance: ${data.balance}
  `;
}

Now you could check the changes on http://localhost:3000/:

Result with balance

All code you can find in this repository: https://github.com/janczer/substrate-nestjs

What next?

You could take a look of polkadot API: polkadot-js/api

If you haven’t run the node locally, you can try this tutorial: Creating Your First Substrate Chain

After that you can try to write your first contract: ink! Smart Contracts Tutorial

Blog
comments powered by Disqus