NestJS/DHive Setup

Words
313
Reading
2 min
Listen
Play
4y

Nest/DHive Project

I consider myself someone functional with typescript, and I noticed there's a typescript library for HIVE. This is me trying it out and setting up a project to stream events and handle them.

Setup

I setup a unnamed-nest-dhive project

Then, I installed NestJS

node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master) [1]> npm i -g @nestjs/cli
added 249 packages, and audited 250 packages in 10s

39 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Next, create my nest project

node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master)> nest new unnamed-nest-dhive
⚡  We will scaffold your app in a few seconds..

CREATE unnamed-nest-dhive/.eslintrc.js (665 bytes)
CREATE unnamed-nest-dhive/.prettierrc (51 bytes)
CREATE unnamed-nest-dhive/README.md (3340 bytes)
CREATE unnamed-nest-dhive/nest-cli.json (118 bytes)
CREATE unnamed-nest-dhive/package.json (2003 bytes)
CREATE unnamed-nest-dhive/tsconfig.build.json (97 bytes)
CREATE unnamed-nest-dhive/tsconfig.json (546 bytes)
CREATE unnamed-nest-dhive/src/app.controller.spec.ts (617 bytes)
CREATE unnamed-nest-dhive/src/app.controller.ts (274 bytes)
CREATE unnamed-nest-dhive/src/app.module.ts (249 bytes)
CREATE unnamed-nest-dhive/src/app.service.ts (142 bytes)
CREATE unnamed-nest-dhive/src/main.ts (208 bytes)
CREATE unnamed-nest-dhive/test/app.e2e-spec.ts (630 bytes)
CREATE unnamed-nest-dhive/test/jest-e2e.json (183 bytes)

? Which package manager would you ❤️  to use? npm
✔ Installation in progress... ☕

��  Successfully created project unnamed-nest-dhive
��  Get started with the following commands:

$ cd unnamed-nest-dhive
$ npm run start

                                                                                                         
                                                                                           Thanks for installing Nest ��
                                                                                  Please consider donating to our open collective
                                                                                         to help us maintain this package.
                                                                                                         
                                                                                                         
                                                                                ��  Donate: https://opencollective.com/nest

RANT Ok. I just want to say, this is silly. I always start with a VCS repo for my project first. I never start with nothing. It seems silly that these scaffoling projects want to assume or insist that you have nothing. No one is going to start a project that way. They always start with a VCS. Can we at least assume that much?

Thanks to nest insisting I start with an empty directory, I now have to copy everything into my VCS path.

node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master)> mv unnamed-nest-dhive/* .
node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master)> ls
nest-cli.json  node_modules/  package.json  package-lock.json  README.md  src/  test/  tsconfig.build.json  tsconfig.json  unnamed-nest-dhive/
node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master)> ls unnamed-nest-dhive/
node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master)> ls -a unnamed-nest-dhive/
./  ../  .eslintrc.js  .git/  .gitignore  .prettierrc
node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master)> mv unnamed-nest-dhive/.eslintrc.js unnamed-nest-dhive/.gitignore unnamed-nest-dhive/.prettierrc .

Install Bluebird

node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master)> npm install -S bluebird

up to date, audited 725 packages in 1s

89 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Then, I installed DHive

node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master)> npm install -S @hiveio/dhive

added 38 packages, and audited 724 packages in 15s

89 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Ok. Easy part done. LOL.

Nest allows us some scaffolding for creating services, so I'm going to create a hive service.

node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master)> nest 
Usage: nest <command> [options]

Options:
  -v, --version                                   Output the current version.
  -h, --help                                      Output usage information.

Commands:
  new|n [options] [name]                          Generate Nest application.
  build [options] [app]                           Build Nest application.
  start [options] [app]                           Run Nest application.
  info|i                                          Display Nest project details.
  add [options] <library>                         Adds support for an external library to your project.
  generate|g [options] <schematic> [name] [path]  Generate a Nest element.
    Schematics available on @nestjs/schematics collection:
      ┌───────────────┬─────────────┬──────────────────────────────────────────────┐
      │ name          │ alias       │ description                                  │
      │ application   │ application │ Generate a new application workspace         │
      │ class         │ cl          │ Generate a new class                         │
      │ configuration │ config      │ Generate a CLI configuration file            │
      │ controller    │ co          │ Generate a controller declaration            │
      │ decorator     │ d           │ Generate a custom decorator                  │
      │ filter        │ f           │ Generate a filter declaration                │
      │ gateway       │ ga          │ Generate a gateway declaration               │
      │ guard         │ gu          │ Generate a guard declaration                 │
      │ interceptor   │ itc         │ Generate an interceptor declaration          │
      │ interface     │ itf         │ Generate an interface                        │
      │ middleware    │ mi          │ Generate a middleware declaration            │
      │ module        │ mo          │ Generate a module declaration                │
      │ pipe          │ pi          │ Generate a pipe declaration                  │
      │ provider      │ pr          │ Generate a provider declaration              │
      │ resolver      │ r           │ Generate a GraphQL resolver declaration      │
      │ service       │ s           │ Generate a service declaration               │
      │ library       │ lib         │ Generate a new library within a monorepo     │
      │ sub-app       │ app         │ Generate a new application within a monorepo │
      │ resource      │ res         │ Generate a new CRUD resource                 │
      └───────────────┴─────────────┴──────────────────────────────────────────────┘

node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master) [1]> nest generate service hive
CREATE src/hive/hive.service.spec.ts (446 bytes)
CREATE src/hive/hive.service.ts (88 bytes)
UPDATE src/app.module.ts (313 bytes)

The scaffolding is great. It generates the stubbed source code and adds everything for me.


import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { HiveService } from './hive/hive.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService, HiveService],
})
export class AppModule {}

Now I setup my DHive client in my hive.service.ts

import { Injectable, Logger } from '@nestjs/common';
import { BlockchainMode, Client, PrivateKey } from '@hiveio/dhive';
import * as Promise from 'bluebird';

@Injectable()
export class HiveService {
  client: any;

  constructor() {
    this.client = new Client('https://api.hive.blog')
  }
}

After that, I add a streamOperations method to my hive.service.ts. This will be important later for streaming events.

    streamOperations(handler, errors): Promise {
        const stream = this.client.blockchain.getOperationsStream();
        stream.on("data", handler)
        stream.on("error", errors)
    }

At this point, my AppService is pretty default and generic.

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

The first thing I do is inject HiveService into the AppService

import { Injectable, Logger } from '@nestjs/common';
import { HiveService } from './hive/hive.service';

@Injectable()
export class AppService {
    private hiveService: HiveService

    constructor(
        hiveService: HiveService,) {
      this.hiveService = hiveService;
    }
}

I change it to add a new run method.

  run() {
    Logger.log("Streaming started")
    const retval = this.hiveService.streamOperations(
        (results) => {
            return Promise.resolve(results.op).spread((operation_name, operation) => {
                switch (operation_name) {
                    case 'comment':
                        break;
                    case 'vote':
                        Logger.log(`${operation.voter} voted on @${operation.author}/${operation.permlink}`)
                        break;
                    case 'unvote':
                        break;
                    case 'transfer':
                        break;
                    case 'comment_benefactor_reward':
                        break;
                    case 'account_update2':
                        break;
                    case 'transfer_to_vesting':
                        break;
                    case 'custom_json':
                        break;
                    case 'feed_publish':
                        break;
                    case 'producer_reward':
                        break;
                    case 'comment_options':
                        break;
                    case 'curation_reward':
                        break;
                    case 'author_reward':
                        break;
                    case 'claim_reward_balance':
                        break;
                    case 'limit_order_create':
                        break;
                    case 'limit_order_cancel':
                        break;
                    case 'claim_account':
                        break;
                    case 'fill_vesting_withdraw':
                        break;
                    case 'fill_order':
                        break;
                    case 'fill_convert_request':
                        break;
                    case 'convert':
                        break;
                    case 'create_claimed_account':
                        break;
                    case 'delegate_vesting_shares':
                        break;
                    case 'account_update':
                        break;
                    case 'witness_set_properties':
                        break;
                    case 'delete_comment':
                        break;
                    case 'account_witness_vote':
                        break;
                    case 'withdraw_vesting':
                        break;
                    case 'proposal_pay':
                        break;
                    case 'sps_fund':
                        break;
                    case 'return_vesting_delegation':
                        break;
                    case 'transfer_from_savings':
                        break;
                    case 'transfer_to_savings':
                        break;
                    case 'fill_transfer_from_savings':
                        break;
                    case 'fill_transfer_to_savings':
                        break;
                    case 'update_proposal_votes':
                        break;
                    case 'change_recovery_account':
                        break;
                    case 'account_create':
                        break;
                    case 'comment_payout_update':
                        break;
                    case 'effective_comment_vote':
                        break;
                    case 'transfer_to_vesting_completed':
                        break;
                    case 'comment_reward':
                        break;
                    case 'delayed_voting':
                        break;
                    case 'witness_update':
                        break;
                    case 'account_witness_proxy':
                        break;
                    case 'expired_account_notification':
                        break;
                    case 'fill_collateralized_convert_request':
                        break;
                    case 'fill_recurrent_transfer':
                        break;
                    case 'changed_recovery_account':
                        break;
                    case 'collateralized_convert':
                        break;
                    case 'clear_null_account_balance':
                        break;
                    case 'set_withdraw_vesting_route':
                        break;
                    case 'cancel_transfer_from_savings':
                        break;
                    case 'failed_recurrent_transfer':
                        break;
                    case 'interest':
                        break;
                    default:
                        Logger.log(`Unknown operation: ${operation_name}: ${JSON.stringify(operation)}`)
                        break;
                }
            })
            .catch((err) => {
                Logger.error("Bot died. Restarting ... ", err)
                Logger.log(`Error Operation ${JSON.stringify(results.op)}`)
            })
        },
        (error) => {
            Logger.error(`Failed ${JSON.stringify(error)}`)
            this.run()
        })
  }

I need to update main.ts now. Originally, NestJS is setup as a web application. It starts a server which is not what I want to do. I just want to stream events, not provide a REST API. I modify main.ts to just call run from the AppService

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AppService } from './app.service';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const appService = app.select(AppModule).get(AppService);
  appService.run()
}
bootstrap();

Now, I can run npm start

node@e0d9a8ccb364 /w/t/unnamed-nest-dhive (master) [1]> npm start

> unnamed-nest-dhive@0.0.1 start
> nest start

[Nest] 10738  - 08/03/2022, 4:55:11 AM     LOG [NestFactory] Starting Nest application...
[Nest] 10738  - 08/03/2022, 4:55:11 AM     LOG [InstanceLoader] AppModule dependencies initialized +23ms
[Nest] 10738  - 08/03/2022, 4:55:11 AM     LOG Streaming started
[Nest] 10738  - 08/03/2022, 4:55:15 AM     LOG fatman voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:15 AM     LOG investegg voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:15 AM     LOG spaminator voted on @brittandjosie/rg0w90
[Nest] 10738  - 08/03/2022, 4:55:15 AM     LOG sleepcult voted on @jovilia/re-life-20220803t044901z
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG thencsshifters voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG steempress voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG rawselectmusic voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG r00sj3 voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG martibis voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG lenasveganliving voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG leveluplifestyle voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG eonwarped voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG darkpylon voted on @creacioneslelys/apoawayj
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG cconn voted on @creacioneslelys/apoawayj
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG tomwafula voted on @creacioneslelys/apoawayj
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG joeyarnoldvn voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG sezermehmet voted on @acidyo/do-you-want-to-watch-an-ad-for-a-boost
[Nest] 10738  - 08/03/2022, 4:55:25 AM     LOG schoolofminnows voted on @clavdio75/le-voyageur-de-la-nuit
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG mrainp voted on @jovilia/re-life-20220803t044901z
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG indigoocean voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG actioncats voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG spydo voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG chisdealhd voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG javyeslava.photo voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG marblely voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG devann voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG ocdb voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG howo voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG senorcoconut voted on @kevinnag58/warning-millions-already-drained-in-continuing-solana-based-wallet-hack
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG jlsplatts voted on @jayke-stringz/check-out-my-introductive-post-on-hive
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG beehivetrader voted on @kevinnag58/warning-millions-already-drained-in-continuing-solana-based-wallet-hack
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG brujita18 voted on @racn/how-i-made-strawberry-mojito
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG gabrielatravels voted on @jayke-stringz/check-out-my-introductive-post-on-hive
[Nest] 10738  - 08/03/2022, 4:55:32 AM     LOG detlev voted on @jayke-stringz/check-out-my-introductive-post-on-hive

That's it. This is a good start for any application. Now you can build any application to respond to events in realtime. I plan to use this going forward to learn about how Hive works through the events.