NestJS/DHive Setup

in #blog2 years ago

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.

Sort:  

I realize there are probably a lot of posts like this out there and that the community is inundated with this information. I think this adds value because I don't believe there are very many on streaming events in such an easy way. It looks like there's a lot involved here, but that's just because I'm being verbose. This really is only 5 min of work by anyone familiar with javascript.

Congratulations @timesheets! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s):

You received more than 10 upvotes.
Your next target is to reach 50 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Check out the last post from @hivebuzz:

Feedback from the August 1st Hive Power Up Day
Hive Power Up Month Challenge 2022-07 - Winners List
The 8th edition of the Hive Power Up Month starts today!
Support the HiveBuzz project. Vote for our proposal!