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.