How to register pg-promise with hapi - javascript

I try to register pg-promise as hapi plugin.
However, the following error occurred.
TypeError: Cannot read properties of undefined (reading 'name')
at internals.Server.register (/home/kim.js/pgpromise/node_modules/#hapi/hapi/lib/server.js:456:42)
at init (/home/kim.js/pgpromise/hapi-pgTest.js:22:16)
My code is as follows and I also attach a reference site.
Hapi Tutorial - Getting Started
Hapi Tutorial - Plugins
'use strict';
const promise = require('bluebird');
const Hapi = require('#hapi/hapi');
const Pg = require('pg-promise')();
const postgreSql = {
host: '127.0.0.1',
port: 5432,
user: 'someone',
password: 'my_password',
database: 'my_db',
table_schema: 'public',
};
const init = async () => {
const server = Hapi.server({
port: 3000,
host: '192.168.9.23'
});
await server.register([{ // ERROR is Here !!
plugin: Pg,
options: {
promiseLib: promise
}
}]);
server.route({
method: 'GET',
path: '/',
handler: async (request, h) => {
let sql = 'SELECT FROM my_table limit 1';
let result = await pgp.one(sql).catch(error => { console.log(sql); });
return result;
}
});
const pgp = Pg(postgreSql);
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();

English is not my first language, so please understand if the sentence is weird.
In my project, I used a plug-in called hapi-pg-promise before, but Hang occurred with hapi v20.
My mistake was that I try register the module, not the plugin.
So I referred to hapi plugin doc and modified my code by referring to hapi-pg-promise source code.
Make My hapi-pg-promise
'use strict';
/* this is original source
const Hoek = require('hoek');
const PgPromise = require('pg-promise');
*/
const Hoek = require('#hapi/hoek');
const PgPromise = require('pg-promise')();
const pkg = require('./package.json');
const DEFAULTS = {
init: {},
cn: undefined
};
module.exports = {
pkg,
register: async function (server, options) {
const opts = Hoek.applyToDefaults(DEFAULTS, options);
const pgp = require('pg-promise')(opts.init);
const db = pgp(opts.cn);
server.ext('onPreHandler', (request, h) => {
request.db = db;
return h.continue;
});
server.expose('db', db);
server.events.on('stop', pgp.end);
}
};
After that, I registered the plugin and it worked :)
/* NOT this
const Pg = require('pg-promise')();
*/
const Pg = require('./myPlugins/my-hapi-pg-promise');

Related

App deployed on Heroku but api calls are now failing (Failed to load resource: net::ERR_CONNECTION_REFUSED, TypeError: Failed to fetch)

My app is successfully deployed on Heroku- it works when I have VSCode open and do npm run start manually, however when I close VSCode it is no longer able to successfully call any APIs on the backend and the console shows me a bunch of errors like the one in the title.
my console (ONLY happens when I close VSCode):
my code in the backend:
const PORT = 8000
const express = require('express')
const cors = require('cors')
const {TwitterApi} = require('twitter-api-v2')
const axios = require('axios')
const cheerio = require('cheerio')
require('dotenv').config()
const snoowrap = require('snoowrap')
const linkPreviewGenerator = require('link-preview-generator')
const spotify = require('spotify-web-api-node')
const fetch = require('node-fetch')
var request = require('request')
const app = express()
app.use(cors())
app.get('/', async (req, res) => {
const client = new TwitterApi(process.env.twt_bearer_token)
const trendsInternational = await client.v1.trendsByPlace(1);
const trendList = []
for (const {trends} of trendsInternational) {
for (const trend of trends) {
trendList.push({
name: trend.name,
url: trend.url
})
}
}
res.json(trendList)
})
app.get('/reddit', async (req, res) => {
const r = new snoowrap({
userAgent: process.env.user_agent,
clientId: process.env.client_id,
clientSecret: process.env.client_secret,
refreshToken: process.env.refresh_token
})
topPosts = []
;(await r.getHot()).forEach(post => {
topPosts.push({
title: post.title,
url: post.url
})
})
res.json(topPosts);
})
app.get('/news', async (req, res) => {
const news_url = 'https://www.theguardian.com/international'
axios(news_url)
.then(response => {
const html = response.data;
const $ = cheerio.load(html);
const articles = [];
const values = new Set();
$('.fc-item__title', html).each(function () { //<-- cannot be a function expression
const title = $(this).text().trim();
const url = $(this).find('a').attr('href');
if (!values.has(url)) {
values.add(url);
articles.push({
title,
url
});
}
})
res.json(articles)
}).catch(err => console.log(err))
})
app.listen(PORT, () => console.log(`Server is running on port ${PORT}`))```
Heroku runs on its own port, try setting port like this
const PORT = Number(process.env["PORT"]) || 8000

express.js app knex.js async connection with aws secrets manager call is firing out of order

I've been stuck on this for days, it feels like no matter how I structure it, the aws secret manager call ALWAYS returns after the app gets initialized. npm run start fires app.js file, this triggers knex and everything runs in order but the secrets response with the username/password seems to come back after everything's already initialized.
knex.js:
console.log("knex.js")
const environment = process.env.APP_ENV || 'development'
const config = require('../knexfile.js')[environment];
const knex = require('knex')(config);
module.exports = knex
knexfile.js:
require('dotenv').config();
const path = require('path')
const pathToCert = path.resolve(__dirname, 'root.cert'); // where to save a file
console.log("knexfile.js")
var AWS = require('aws-sdk');
region = 'us-east-2'
secretName = 'blah'
var client = new AWS.SecretsManager({
region: region
});
async function getConfig() {
console.log("in get config")
return await new Promise((resolve, reject) => {
client.getSecretValue({ SecretId: secretName }, function (
err,
data
) {
let secret = JSON.parse(data.SecretString);
console.log("returning it***************" + secret.password + " " + secret.username + " " + pathToCert)
let connectionString = `postgresql://${secret.username}:${secret.password}#some-host:1234/defaultdb?sslmode=verify-full&sslrootcert=${pathToCert}&options=--cluster`
resolve(connectionString)
return
})
})
}
const config = {
development: {
client: 'pg',
version: 7.2,
connection: getConfig(),
migrations: {
directory: './knex/migrations',
},
seeds: {
directory: './knex/seeds'
}
}
}
console.log("exporting module")
module.exports = config
The console returns:
knex.js
knexfile.js
in get config
exporting module
Listening on: 3000
returning it***************pass username C:\project-webservice\root.cert
Does anyone see what I'm doing wrong?
Of course right after I post I figure it out :)
Right from knex documentation, I adjusted my knexfile.js based off of the following code snippet:
const knex = require('knex')({
client: 'postgres',
connection: async () => {
const { token, tokenExpiration } = await someCallToGetTheToken();
return {
host : 'your_host',
port : 3306,
user : 'your_database_user',
password : token,
database : 'myapp_test',
expirationChecker: () => {
return tokenExpiration <= Date.now();
}
};
}
});
specifically:
async () => {
const { token, tokenExpiration } = await someCallToGetTheToken();
return {
host : 'your_host',
port : 3306,
user : 'your_database_user',
password : token,
database : 'myapp_test',
expirationChecker: () => {
return tokenExpiration <= Date.now();
}
};

Web3 web3.eth.sendSignedTransaction Invalid params

Im very new to ethereum i have set up a private network using the pantheon client. i have successfully deployed a contract to the network and all interactions with the contract work when using through remix.
I am trying to set up a relay where a transaction is signed client side, sent to a nodeJs server and then the server proxies the transaction to the contract. however when i pass the signed transaction to sendSignedTransaction() i get the error Invalid params, to me this is very vague and i am unsure what i'm doing wrong / what the invalid params are. (any advice on how to debug this?)
UPDATE
using web3 v1.2.0
Error
Error: Returned error: Invalid params
at Object.ErrorResponse (/Users/ghost/node_modules/web3-core-helpers/src/errors.js:29:16)
at Object.<anonymous> (/Users/ghost/node_modules/web3-core-requestmanager/src/index.js:140:36)
at /Users/ghost/node_modules/web3-providers-ws/src/index.js:121:44
at Array.forEach (<anonymous>)
at W3CWebSocket.WebsocketProvider.connection.onmessage (/Users/ghost/node_modules/web3-providers-ws/src/index.js:98:36)
at W3CWebSocket._dispatchEvent [as dispatchEvent] (/Users/ghost/node_modules/yaeti/lib/EventTarget.js:107:17)
at W3CWebSocket.onMessage (/Users/ghost/node_modules/websocket/lib/W3CWebSocket.js:234:14)
at WebSocketConnection.<anonymous> (/Users/ghost/node_modules/websocket/lib/W3CWebSocket.js:205:19)
at WebSocketConnection.emit (events.js:188:13)
at WebSocketConnection.processFrame (/Users/ghost/node_modules/websocket/lib/WebSocketConnection.js:552:26)
at /Users/ghost/node_modules/websocket/lib/WebSocketConnection.js:321:40
at process.internalTickCallback (internal/process/next_tick.js:70:11)
Contract
pragma solidity ^0.5.1;
import "./Ownable.sol";
contract Entry is Ownable {
mapping (address => string) hash;
function addEntry(string memory _hash) public {
hash[msg.sender] = _hash;
}
function getHash() public view returns(string memory){
return hash[msg.sender];
}
}
Relay Server
const Web3 = require('web3');
const express = require('express')
const app = express()
const port = 3003
const bodyParser = require('body-parser');
const cors = require('cors')
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json())
app.use(cors())
var web3 = new Web3(Web3.givenProvider || "ws://localhost:7002");
app.post('/transaction/send', async (req, res) => {
const {tx, data} = req.body;
web3.eth.sendSignedTransaction(tx, function (err, transactionHash) {
if(err) console.log(err);
console.log(transactionHash);
});
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
Front end
import React from 'react';
import './App.css';
import Web3 from 'web3';
import request from 'request-promise';
const Tx = require('ethereumjs-tx').Transaction;
const web3 = new Web3("http://localhost:8545");
const privKey = '[My Priv key here]';
const contractADDRESS = "0x4261d524bc701da4ac49339e5f8b299977045ea5";
const addressFrom = '0x627306090abaB3A6e1400e9345bC60c78a8BEf57';
const contractABI = [{"constant":false,"inputs":[{"name":"_hash","type":"string"}],"name":"addEntry","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getHash","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}];
function App() {
async function sendTx(){
const data = await extraData();
web3.eth.getTransactionCount(addressFrom).then(txCount => {
const txData = {
nonce: web3.utils.toHex(txCount),
gasLimit: web3.utils.toHex(25000),
gasPrice: web3.utils.toHex(10e9),
to: contractADDRESS,
from: addressFrom,
data: data
}
sendSigned(txData, function(err, result) {
if (err) return console.log('error', err)
console.log('sent', result)
})
})
}
async function sendSigned(txData, cb) {
const privateKey = new Buffer(privKey, 'hex')
const transaction = new Tx(txData)
transaction.sign(privateKey)
const serializedTx = transaction.serialize().toString('hex')
const response = request({
method: 'POST',
uri: 'http://127.0.0.1:3003/transaction/send',
body: {
tx: serializedTx,
data: 'somehashhh'
},
json: true,
});
}
async function extraData() {
const contractInstance = new web3.eth.Contract(contractABI, contractADDRESS);
return await contractInstance.methods.addEntry('somehashhh').encodeABI();
}
return (
<div className="App">
<header className="App-header">
<div onClick={() => sendTx()}>Submit transaction</div>
</header>
</div>
);
}
export default App;
This is the txData sent from front end
{
data: "0x17ce42bd0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a736f6d6568617368686800000000000000000000000000000000000000000000"
from: "0x627306090abaB3A6e1400e9345bC60c78a8BEf57"
gasLimit: "0x61a8"
gasPrice: "0x2540be400"
nonce: "0x0"
to: "0x4261d524bc701da4ac49339e5f8b299977045ea5"
}
After a lot of trail and error an 0 suggestions on stack overflow working, i have got the transaction signing working!. In the end i came away from using ethereumjs-tx (which for some reason is recommended by a lot of people) and used just pure Web3.
Front end client
async function sendTx(){
const { address: from } = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY)
const contract = new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS)
const query = await contract.methods.updateCount();
const signed = await web3.eth.accounts.signTransaction({
to: CONTRACT_ADDRESS,
from,
value: '0',
data: query.encodeABI(),
gasPrice: web3.utils.toWei('20', 'gwei'),
gas: Math.round((await query.estimateGas({ from })) * 1.5),
nonce: await web3.eth.getTransactionCount(from, 'pending')
}, PRIVATE_KEY)
const response = await request({
method: 'POST', json: true,
uri: 'http://127.0.0.1:3003/transaction/send',
body: {
tx: signed.rawTransaction,
data: 'some data'
}
});
console.log(response);
}
Relay Server
const Web3 = require('web3');
const express = require('express')
const app = express()
const port = 3003
const bodyParser = require('body-parser');
const cors = require('cors')
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json())
app.use(cors())
var web3 = new Web3(Web3.givenProvider || "ws://localhost:7002");
app.post('/transaction/send', async (req, res) => {
const {tx, data} = req.body;
web3.eth.sendSignedTransaction(tx)
.on('transactionHash', (txHash) => res.json({txHash}))
.on('error', console.log)
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
hopefully this can help someone else
👍

Error while testing delete method in NodeJs with Mocha

I'm developing a billboard app to learn new technologies and i'm currently having problems trying to test the DELETE method of the API using Mocha.
I've been trying different approaches for it, but i couldn't find a solution yet. I'm using NodeJs and Hapi for the back-end server
const mongoose = require('mongoose')
const chai = require('chai')
const hapiServer = require('../../../index')
const expect = require('chai').expect
const should = chai.should()
chai.use(require('chai-http'))
before(async () => {
const server = await hapiServer()
global.url = 'http://localhost:3000'
})
after(async () => {
await mongoose.connection.close()
//await mongoose.connection.db.dropCollection()
})
const id = new mongoose.Types.ObjectId('5cd8a0eefc06344accd62a76')
describe('Movies API', () => {
it('Should DELETE a single movie', async () => {
const response = await chai.request(global.url)
.delete('/api/movies/' + '5cd8a0eefc06344accd62a77')
response.should.have.status(202)
})
})
Here's the index.js
const Hapi = require('#hapi/hapi')
const Inert = require('#hapi/inert')
const Vision = require('#hapi/vision')
const HapiSwagger = require('hapi-swagger')
const mongoose = require('mongoose')
const mongo = require('./config/mongo')
const Pack = require('./package')
const start = async () => {
const server = await new Hapi.Server({
host: 'localhost',
port: 3000,
})
// MongoDB Connection
try{
await mongoose
.connect(mongo.configuration.getUri(process.env.NODE_ENV), {useNewUrlParser: true})
console.log('MongoDB Connected...')
} catch(err) {
console.log(err)
}
const swaggerOptions = {
info: {
title: 'Billboard API Documentation',
version: Pack.version,
},
}
await server.register([
Inert,
Vision,
{
plugin: HapiSwagger,
options: swaggerOptions
}
]);
try {
await server.start()
console.log('Server running at:', server.info.uri)
} catch(err) {
console.log(err)
}
// Register Plugins
const moviesRoutes = require('./plugins/movies/routes')
server.route(moviesRoutes)
}
start()
module.exports = start
So, the the rest of the api tests are similar and they work excelent. But when i try to test this method i get this response:
error: Error: cannot DELETE /api/movies/5cd8a0eefc06344accd62a77 (404)
at Response.toError (C:\Users\lucas\billboard-backend\node_modules\superagent\lib\node\response.js:94:15)
at ResponseBase._setStatusProperties (C:\Users\lucas\billboard-backend\node_modules\superagent\lib\response-base.js:123:16)
at new Response (C:\Users\lucas\billboard-backend\node_modules\superagent\lib\node\response.js:41:8)
at Test.Request._emitResponse (C:\Users\lucas\billboard-backend\node_modules\superagent\lib\node\index.js:752:20)
at C:\Users\lucas\billboard-backend\node_modules\superagent\lib\node\index.js:916:38
at IncomingMessage.<anonymous> (C:\Users\lucas\billboard-backend\node_modules\superagent\lib\node\parsers\json.js:19:7)
at IncomingMessage.emit (events.js:201:15)
at endReadableNT (_stream_readable.js:1130:12)
at processTicksAndRejections (internal/process/task_queues.js:84:9) {
status: 404,
text: '{"statusCode":404,"error":"Not Found","message":"Not Found"}',
method: 'DELETE',
path: '/api/movies/5cd8a0eefc06344accd62a77'
Any ideas of why is this happening?
You need to add a route like this in your routes file assuming you're using controllers
const express = require('express')
const router = express.Router()
const movieController = require('../controllers/movie.controller')
router.delete('/api/movies/:movieId', movieController.deleteMovie)
module.exports = router

Node.js Async/Await module export

I'm kinda new to module creation and was wondering about module.exports and waiting for async functions (like a mongo connect function for example) to complete and exporting the result. The variables get properly defined using async/await in the module, but when trying to log them by requiring the module, they show up as undefined. If someone could point me in the right direction, that'd be great. Here's the code I've got so far:
// module.js
const MongoClient = require('mongodb').MongoClient
const mongo_host = '127.0.0.1'
const mongo_db = 'test'
const mongo_port = '27017';
(async module => {
var client, db
var url = `mongodb://${mongo_host}:${mongo_port}/${mongo_db}`
try {
// Use connect method to connect to the Server
client = await MongoClient.connect(url, {
useNewUrlParser: true
})
db = client.db(mongo_db)
} catch (err) {
console.error(err)
} finally {
// Exporting mongo just to test things
console.log(client) // Just to test things I tried logging the client here and it works. It doesn't show 'undefined' like test.js does when trying to console.log it from there
module.exports = {
client,
db
}
}
})(module)
And here's the js that requires the module
// test.js
const {client} = require('./module')
console.log(client) // Logs 'undefined'
I'm fairly familiar with js and am still actively learning and looking into things like async/await and like features, but yeah... I can't really figure that one out
You have to export synchronously, so its impossible to export client and db directly. However you could export a Promise that resolves to client and db:
module.exports = (async function() {
const client = await MongoClient.connect(url, {
useNewUrlParser: true
});
const db = client.db(mongo_db);
return { client, db };
})();
So then you can import it as:
const {client, db} = await require("yourmodule");
(that has to be in an async function itself)
PS: console.error(err) is not a proper error handler, if you cant handle the error just crash
the solution provided above by #Jonas Wilms is working but requires to call requires in an async function each time we want to reuse the connection. an alternative way is to use a callback function to return the mongoDB client object.
mongo.js:
const MongoClient = require('mongodb').MongoClient;
const uri = "mongodb+srv://<user>:<pwd>#<host and port>?retryWrites=true";
const mongoClient = async function(cb) {
const client = await MongoClient.connect(uri, {
useNewUrlParser: true
});
cb(client);
};
module.exports = {mongoClient}
then we can use mongoClient method in a diffrent file(express route or any other js file).
app.js:
var client;
const mongo = require('path to mongo.js');
mongo.mongoClient((connection) => {
client = connection;
});
//declare express app and listen....
//simple post reuest to store a student..
app.post('/', async (req, res, next) => {
const newStudent = {
name: req.body.name,
description: req.body.description,
studentId: req.body.studetId,
image: req.body.image
};
try
{
await client.db('university').collection('students').insertOne({newStudent});
}
catch(err)
{
console.log(err);
return res.status(500).json({ error: err});
}
return res.status(201).json({ message: 'Student added'});
};

Categories