I have the following AWS lambda handler:
exports.handler = async (event, data) => {
var AWS = require('aws-sdk/global');
const awsParamStore = require('aws-param-store');
const {
Pool
} = require('pg');
var host;
await awsParamStore.getParameter('testing-main-rds-url', { //get database host from AWS
region: 'us-east-1'
})
.then((parameter) => {
host = parameter.Value
});
var signer = await new AWS.RDS.Signer({
region: 'us-east-1',
username: 'developer',
hostname: host,
port: 5432
});
var token = await signer.getAuthToken({}); //get token from AWS
const db = await new Pool({
user: "developer",
host: host,
database: "main",
password: token,
port: 5432,
ssl: true,
});
const query = `INSERT INTO crm.user_crm
(u_id,username)
VALUES ($1,$2)`;
const values = [event.u_id, event.username];
db.connect((err, client, release) => {
if (err) {
throw("Error acquiring client.", err.stack);
} else {
client.query(query, values, (err, result) => {
release();
if (err) {
throw("Error executing query.", err.stack);
return ;
} else {
console.log("INSERT DONE");
db.end()
return {
statusCode: 200
};
}
})
}
})
};
This code will take in data and add it to a database.
When I run it on my computer with console.log(require('./index').handler(data)); it works perfectly and inserts the record. When I run it from lambda it returns nothing and doesn’t insert a record. Any help would be appreciated.
You mixing async/await with callback style. Your lambda function will finish before the query finish.
As example in pg document it supports async/await:
Instead of db.connect((err, client, release) => {...
const res = await pool.query(query, values);
console.log("INSERT DONE");
await pool.end()
return {
statusCode: 200
};
Related
I'm new to Serverless with NodeJS, I'm trying to use an Async function to hash a password and perform some stuff in a database, the problem is that when I declare the functions async I always get this error:
But if I remove the async keyword here:
module.exports.login = async (event, context, callback) => {
the function runs properly, but of course I won't be able to use promises within the function.
This is an endpoint, to make API calls.
Here's my code:
'use strict';
require('dotenv').config({ path: './.env' });
const dataBase = require('./utils/db');
const bcrypt = require('bcrypt');
const encryptPassword = async (plainPassword) => {
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(plainPassword, saltRounds);
return hashedPassword;
};
module.exports.login = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
const parsedBody = JSON.parse(event.body);
const connect = dataBase.connectToDatabase();
const newPassword = await encryptPassword('test');
console.log(newPassword);
connect.query('SELECT * FROM users', (error, results, fields) => {
if (error) {
console.error(error);
callback(null, {
statusCode: 500,
body: JSON.stringify({
error: JSON.stringify(error),
message: 'Internal Server Error Booh!',
}),
});
}
if (results) {
callback(null, {
statusCode: 200,
body: JSON.stringify({
error: results,
message: 'No Error',
}),
});
}
});
};
Here my db connection and config:
db.js
const mysql = require('mysql');
const isDev = true;
// Create mySQL Connection
const connectToDatabase = () => {
const pool = mysql.createPool({
host: isDev ? process.env.DATABASE_HOST_DEV : process.env.DATABASE_HOST_PROD,
user: isDev ? process.env.DATABASE_USER_DEV : DATABASE_USER_PROD,
password: isDev ? process.env.DATABASE_PASSWORD_DEV : DATABASE_PASSWORD_PROD,
database: isDev ? process.env.DATABASE_DATABASE_DEV : DATABASE_DATABASE_PROD,
multipleStatements: true,
});
return pool;
};
exports.connectToDatabase = connectToDatabase;
exports.mysql = mysql;
What am I missing?
EDIT:
The purpose of all this, it's because I'm learning serverless.
All this will end up in an AWS Lambda with an endpoint using API Gateway.
So, when you call this endpoint, you will be sending some params to register/login into an app.
I need to use async/await because if you register an account, the password needs to be hashed and then store into a database or if you login, the password will need to be compared, both of these actions are asynchronous ones.
That's why I need the endpoint to be an async function.
EDIT 2:
Reading this post: https://github.com/netlify/netlify-dev-plugin/issues/160
As Phil mention, async and callback shouldn't be used togehter, so I modified my code like this, and same error:
'use strict';
require('dotenv').config({ path: './.env' });
const dataBase = require('./utils/db');
const bcrypt = require('bcrypt');
const encryptPassword = async (plainPassword) => {
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(plainPassword, saltRounds);
return hashedPassword;
};
module.exports.login = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
const parsedBody = JSON.parse(event.body);
const connect = dataBase.connectToDatabase();
const newPassword = await encryptPassword('test');
console.log(newPassword);
connect.query('SELECT * FROM users', (error, results, fields) => {
if (error) {
console.error(error);
return {
error: 'some error to test',
};
}
if (results) {
return {
body: 'someBody',
};
}
});
};
FINAL FUNCTIONAL CODE:
In case any wonder how to make these queries as promises, here's my approach, hope it helps anyone out there struggling with the same.
'use strict';
require('dotenv').config({ path: './.env' });
const dataBase = require('./utils/db');
const bcrypt = require('bcrypt');
const encryptPassword = async (plainPassword) => {
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(plainPassword, saltRounds);
return hashedPassword;
};
module.exports.login = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
const parsedBody = JSON.parse(event.body);
const connect = dataBase.connectToDatabase();
const hashedPassword = await encryptPassword('test');
console.log(hashedPassword);
return new Promise((resolve, reject) => {
connect.query('SELECT * FROM users', (error, result) => {
if (error) {
reject(
callback(null, {
statusCode: 500,
body: JSON.stringify({
error: JSON.stringify(error),
message: 'Internal Server Error Booh!',
}),
})
);
}
if (result) {
resolve(
callback(null, {
statusCode: 200,
body: JSON.stringify({
data: result,
message: 'No Error',
}),
})
);
}
});
});
};
Regards.
Taking a quick look at the docs, it doesn't look like you should be mixing async with the callback arg. Just use one or the other, not both.
You could migrate to the mysql2 library so you can use promises or stick with the one you've got and use encryptPassword("test").then() instead of await
// no async
module.exports.login = (event, context, callback) => {
// remove this, you don't want it
// context.callbackWaitsForEmptyEventLoop = false;
const parsedBody = JSON.parse(event.body);
const connect = dataBase.connectToDatabase();
encryptPassword("test").then(newPassword => { // use .then()
console.log(newPassword);
// callback APIs are a pain, recommend migrating to mysql2 and use promises
connect.query('SELECT * FROM users', (error, results, fields) => {
if (error) {
console.error(error);
return callback(null, {
statusCode: 500,
body: JSON.stringify({
error: JSON.stringify(error),
message: 'Internal Server Error Booh!',
}),
});
}
callback(null, {
statusCode: 200,
body: JSON.stringify({
error: results, // error?
message: 'No Error',
}),
});
}
});
}
I want to access to Azure Postgres DB from an Azure function.
Below is the node.js source code of Azure function.
module.exports = async function (context, req) {
try {
context.log('Start function!');
var pg = require('pg');
const config = {
host: 'taxiexample.postgres.database.azure.com',
user: 'postgres#taxiexample',
password: 'QscBjk10;',
database: 'taxi',
port: 5432,
ssl: false
};
var client = new pg.Client(config);
const query = 'insert into taxi values (\'2\');';
context.log(query);
client.connect();
var res = client.query(query);
await client.end();
context.log(res);
} catch (e) {
context.log(e);
} finally {
context.res = {
status: 200,
body: "ok"
};
}
};
The record doesn't insert, the res object returns the following error:
2020-03-04T23:03:59.804 [Information] Promise {
<rejected> Error: Connection terminated
at Connection.<anonymous> (D:\home\site\wwwroot\HttpTrigger1\node_modules\pg\lib\client.js:254:9)
at Object.onceWrapper (events.js:312:28)
at Connection.emit (events.js:228:7)
at Socket.<anonymous> (D:\home\site\wwwroot\HttpTrigger1\node_modules\pg\lib\connection.js:78:10)
at Socket.emit (events.js:223:5)
at TCP.<anonymous> (net.js:664:12)
}
2020-03-04T23:03:59.811 [Information] Executed 'Functions.HttpTrigger1' (Succeeded, Id=944dfb12-095d-4a28-a41d-555474b2b0ee)
Can you help me? Thanks
I've resolve it, it was a trivial programming error.
Below is the correct source code
module.exports = async function (context, req) {
try {
context.log('Start function!');
var pg = require('pg');
const config = {
host: 'example.postgres.database.azure.com',
user: 'postgres#example',
password: 'passwd;',
database: 'test',
port: 5432,
ssl: true
};
var client = new pg.Client(config);
const query = 'insert into test values (\'2\');';
context.log(query);
client.connect(err => {
if (err) {
console.error('connection error', err.stack);
} else {
console.log('connected');
client.query(query, (err, res) => {
if (err) throw err;
console.log(res);
client.end();
})
}
});
} catch (e) {
context.log(e);
} finally {
context.res = {
status: 200,
body: "ok"
};
}
};
I am using Hapi.js and have a route that I want to use to fetch data and then return a result.
I have tried to use async/await, but I must be doing something wrong because while the function I am calling eventually prints a result to the console, the route is returning without waiting for that function to return a value.
'use strict';
const Hapi = require('#hapi/hapi');
const HandyStorage = require('handy-storage');
var ethBalance ='';
// Connection to public blockchain via Infura.io
const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/cf44bc52af3743bcad5f0b66813f8740"));
// Initialize Handy Storage
const storage = new HandyStorage({
beautify: true
});
//Get ETH address from Handy Storage
storage.connect('./preferences.json');
var walletAddress = storage.state.wallet;
// Get wallet balance
const getWalletBalance = async () => {
web3.eth.getBalance(`${walletAddress}`, async function(err, result) {
if (err) {
console.log('There was an error: ' + err);
return ({ error: 'The wallet balance call failed.' });
} else {
ethBalance = await web3.utils.fromWei(result, "ether");
console.log("This should be first: The wallet balance via API call is " + ethBalance + " ETH.");
return ethBalance; // I expect the walletbalance route to wait for this to be returned
}
});
};
// API Server
const init = async () => {
// Connection settings
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
// Get wallet balance
server.route({
method: 'GET',
path: '/walletbalance/',
handler: async (request, h) => {
let result = null;
try {
result = await getWalletBalance();
console.log('This should be second, after the getWalletBalance function has printed to the console.'); // this prints first, so await isn't working as expected
return ({ ethBalance: result });
} catch (err) {
console.log('Error in walletbalance route');
}
}
});
// 404 error handling
server.route({
method: '*',
path: '/{any*}',
handler: function (request, h) {
return ({
message: 'Error!'
});
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
Any idea where I have gone wrong here? This is the first time I have used async/await.
ETA: My console looks like this:
[nodemon] starting `node index.js`
Server running on http://localhost:3000
This should be second, after the getWalletBalance function has printed to the console.
This should be first: The wallet balance via API call is 4061.894069996147660079 ETH.
And this is the JSON I get back when I use the wallet balance route:
{}
Based on the answer I was given, I was able to get the results I wanted with this:
'use strict';
const Hapi = require('#hapi/hapi');
const HandyStorage = require('handy-storage');
var ethBalance ='';
// Connection to public blockchain via Infura.io
const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/cf44bc52af3743bcad5f0b66813f8740"));
// Initialize Handy Storage
const storage = new HandyStorage({
beautify: true
});
//Get ETH address from Handy Storage
storage.connect('./preferences.json');
var walletAddress = storage.state.wallet;
// Get wallet balance
async function getWalletBalance(){
let ethBalance = await web3.eth.getBalance(`${walletAddress}`);
if (ethBalance.err) {
console.log('error in the called function');
} else {
return ethBalance;
}
}
// API Server
const init = async () => {
// Connection settings
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
// Get wallet balance
server.route({
method: 'GET',
path: '/walletbalance/',
handler: async (request, h) => {
try {
const result = await getWalletBalance();
const ethBalanceInWei = web3.utils.fromWei(result, "ether");
return ({ balance: ethBalanceInWei });
} catch (err) {
console.log('Error in walletbalance route');
}
}
});
// 404 error handling
server.route({
method: '*',
path: '/{any*}',
handler: function (request, h) {
return ({
message: 'Error!'
});
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
Thank you for the help! That got me going in the right direction.
Basically your getWalletBalance function is using multiple concepts. callback style functions and inside that you are using await. I have restructured your code a little bit. Hopefully that should fix the issue which you are facing.
'use strict';
const Hapi = require('#hapi/hapi');
const HandyStorage = require('handy-storage');
var ethBalance ='';
// Connection to public blockchain via Infura.io
const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/cf44bc52af3743bcad5f0b66813f8740"));
// Initialize Handy Storage
const storage = new HandyStorage({
beautify: true
});
//Get ETH address from Handy Storage
storage.connect('./preferences.json');
var walletAddress = storage.state.wallet;
function getWalletBalance() {
return Promise((resolve, reject) => {
web3.eth.getBalance(`${walletAddress}`, (err, result) => {
if (err) {
console.log('There was an error: ' + err);
reject({ error: 'The wallet balance call failed.' });
} else {
resolve(result);
}
});
});
}
// API Server
const init = async () => {
// Connection settings
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
// Get wallet balance
server.route({
method: 'GET',
path: '/walletbalance/',
handler: async (request, h) => {
try {
const result = await getWalletBalance();
ethBalance = await web3.utils.fromWei(result, "ether");
return ethBalance;
} catch (err) {
console.log('Error in walletbalance route');
}
}
});
// 404 error handling
server.route({
method: '*',
path: '/{any*}',
handler: function (request, h) {
return ({
message: 'Error!'
});
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
I'm running the below code seems like the connection is made successfully but i do not see any output or is there any way to see the output of the query result, I'm new to this protractor nodeJS MSSQL connection.
const assert = require("../configuration.js");
const { config } = require("dotenv");
const { ConnectionPool } = require("mssql");
var sql = require('mssql');
config();
const c = {
driver: 'msnodesqlv8',
server: "server/nameORIP",
user: "UserName",
password: "Password",
database: 'DBname',
};
describe("mssql connection", () => {
it('test query', () => {
sql.connect(config, (err) => {
if (err) console.log(err);
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query('select * from NetUsers', (err, recordset) => {
if (err) console.log(err)
// send records as a response
request.send(recordset);
});
}); // end of sql.connect()
}) // end of it
})
Output
Request {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
canceled: false,
_paused: false,
parent: [Function: ConnectionPool],
parameters: {},
rowsAffected: 0 }
I didn't find any information for the method request.send(recordset); from their official documentation.
Supported methods in Request Object are:
execute - https://www.npmjs.com/package/mssql#execute
input - https://www.npmjs.com/package/mssql#input
output - https://www.npmjs.com/package/mssql#output
pipe - https://www.npmjs.com/package/mssql#pipe
query - https://www.npmjs.com/package/mssql#query
batch - https://www.npmjs.com/package/mssql#batch
bulk - https://www.npmjs.com/package/mssql#bulk
cancel - https://www.npmjs.com/package/mssql#cancel
request.query function will give you the list of records from the DB and pass it to the callback function and the values can be accessed directly using
request.query('select * from NetUsers', (err, resultArray) => {
if (err) console.log(err)
// if no error
console.log(resultArray.recordset[0]); //will print the first row from response.
});
Refer : https://www.npmjs.com/package/mssql#query-command-callback
I finally made it to work with few changes not only it connects but also runs the query and shows me the output in console, once again thank you for all the help.
import { async } from "q";
const sql = require('mssql/msnodesqlv8');
const assert = require("../configuration.js");
const poolPromise = new sql.ConnectionPool({
driver: 'msnodesqlv8',
server: "server/nameORIP",
user: "UserName",
password: "Password",
database: 'DBname',
})
.connect()
.then(pool => {
console.log('Connected to MSSQL')
return pool
})
.catch(err => console.log('Database Connection Failed! Bad Config: ', err))
;
describe('any test', () => {
it('verification', async () => {
try
{
const pool = await poolPromise;
const result = await pool.request()
.query('SELECT TOP (10) * FROM dbo.NetUsers')
console.log(result);
}
catch(err)
{
console.log(err);
}
});
});
I have the following two JS file. My problem is when i call the Calls.js which calls the Archive.js for archiving logs into DynamoDB the request times out.
I have tried out, many things, read about many things, tried in local/AWS environment without luck. What am i missing?
Link1, Link2, Link3, Link4, Link5,
Archive.js
module.exports.archive = archive;
...
function archive(input, callback){
AWS.config.update({
region: "eu-west-1",
endpoint: "http://localhost:8000"
});
var documentClient = new AWS.DynamoDB.DocumentClient({
httpOptions: {
agent: new https.Agent({
rejectUnauthorized: true,
secureProtocol: "TLSv1_method",
ciphers: "ALL"
})
}
});
...
var paramsPUT = {
TableName: "Logging",
Item: {
HashKey: dbID,
archiveEntry: archiveEntry
}
};
...
documentClient.put(paramsPUT, function(err, data) {
if (err) console.log(err);
if (data) console.log(data);
...
callback(data);
});
}
Calls.js
exports.handler(event, context, callback) => {
const archive = require("./..path..").archive;
...
context.callbackWaitsForEmptyEventLoop = false;
...
archive(input, callback);
...
}
I can not reproduce a timeout condition with your code. Your code is talking to an AWS endpoint at http://localhost:8000, so I assume you have DynamoDB local up and running, don't you ? Failling to have local DynamoDB running would cause the timeout.
That being said, I would strongly suggest to refactor your code to use Promise and the new async/await provided by NodeJS 8 instead of passing the Lambda callback around.
Here is the modified code.
const AWS = require("aws-sdk");
async function archive(input) {
return new Promise( (resolve, reject) => {
AWS.config.update({
region: "eu-west-1",
endpoint: 'http://localhost:8000'
});
//use client specific AWS configuration instead of the global one
const documentClient = new AWS.DynamoDB.DocumentClient();
var paramsPUT = {
TableName: "Logging",
Item: {
HashKey: "123",
archiveEntry: input
}
};
documentClient.put(paramsPUT, function (err, data) {
if (err) {
console.log("ERROR " + err);
reject(err);
}
console.log("Returned from DDB " + JSON.stringify(data, null,2));
resolve(data);
});
});
}
exports.handler = async (event, context, callback) => {
const result = await archive("abc");
callback(result);
}
// stuffs to test locally
callback = function (data) {
console.log("callback called with " + JSON.stringify(data,null,2));
}
event = context = {}
exports.handler(event, context, callback);