I'm new to node.js and trying to create a lambda function that queries a collection from MongoDB.
Here is a code I found as a starting point:
'use strict';
const MongoClient = require('mongodb').MongoClient;
const ATLAS_URI = "mongodb://lambdaUser:PASSWORD#cluster0-shard-00-00-ddlwo.mongodb.net:27017,cluster0-shard-00-01-ddlwo.mongodb.net:27017,cluster0-shard-00-02-ddlwo.mongodb.net:27017/mydb?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin";
let cachedDb = null;
function connectToDatabase(uri) {
console.log('=> connect to database');
if (cachedDb) {
console.log('=> using cached database instance');
return Promise.resolve(cachedDb);
}
return MongoClient.connect(uri)
.then(db => { cachedDb = db; return cachedDb; });
}
function queryDatabase(db) {
console.log('=> query database');
return db.collection('sensordata').find({}).toArray()
.then(() => { return { statusCode: 200, body: 'success' }; })
.catch(err => { return { statusCode: 500, body: 'error' }; });
}
exports.handler = (event, context, callback) => {
connectToDatabase(ATLAS_URI)
.then(db => queryDatabase(db))
.then(result => {
console.log('=> returning result: ', result);
context.succeed(result);
})
.catch(err => {
console.log('=> an error occurred: ', err);
context.failed(err);
});
};
This code works fine, but I don't know how to recover the data from the query...
Looking other code I see there is a function(err,data) inside the find(), but in this case I don't know how to insert that or modify the code to return the data instead of the {statuscode: 200, body: 'success'} json object.
I would appreciate any help.
Thanks
Gus
Related
When i try to get result from node-postgres from my express app, pool.query is returning undefined result when i log it in console. Not sure if its about database connected properly or I am not returning the result properly? I am using heroku to deploy the app and using connection string given by heroku. Cant figure it out, anyone there to help please?.
db.js:
const { Pool } = require('pg');
const conString = process.env.DATABASE_URL;
const pool = new Pool({
connectionString: conString,
ssl: {rejectUnauthorized: false}
});
module.exports ={
getResult: (sql, params, callback)=>{
pool.query(sql, [params], (error, results)=>{
console.log(results);
if(!error){
callback(results);
}else{
callback(null);
}
pool.end();
});
}
}
user-model.js
var db = require('./db');
module.exports ={
findUserById: (userId)=>{
return new Promise((resolve, reject)=>{
var sql = "select id from users where id=?";
db.getResult(sql, [userId], (result)=>{
if(result.length>0){
resolve(true);
}else{
resolve(false);
}
});
});
}
}
seems the sent query parameter is in mysql format, use node-postgres format which is var sql = "select id from users where id = $1"; which should return valid result
It seems that your use of params is not correct.
You're passing an array to db.getResult(), then using it as the first element of another array.
Should just be pool.query(sql, params, (error, results)=>{ on that line.
you need to get the pool connection
const pool = require('./pool');
module.exports = {
// my version
findUserById(sql, params) {
return new Promise((resolve, reject) => {
return pool
.connect()
.then(conn => {
conn
.query(sql, [params])
.then(result => {
conn.release()
resolve(result)
})
.catch(error => {
conn.release()
reject(error)
})
})
})
},
// your version
findUserByIds: (userId) => {
return new Promise((resolve, reject) => {
var sql = "select id from users where id=?";
db.getResult(sql, [userId], (result) => {
if (result.length > 0) {
resolve(true);
} else {
resolve(false);
}
});
});
}
}
//// in you main or you controller file
// use the function
const { findUserById } = require('./model')
app.get('/user/:id', (req, res) => {
let sql = 'select * from "users" where "userId"= $1';
findUserById(sql, 1)
.then(result => {
res.status(200).send({
data: result
})
})
.catch(error => {
res.status(400).send(error)
})
})
I'm refactoring my code to remove a "callback hell" using Promises, but encountered an error that I cannot pass. My code receives list of IDs and processes them making few database calls, that is why I had this "callback hell".
Everything worked fine until Promises. The res is equal 0 when I had to respond back to the client.
function processVMDelete(returnedVMIDs){
return new Promise((resolve, reject) => {
var mariasqlClient = dbConnection();
mariasqlClient.query( sqlUpdateDELETE_STATE_ByVMID, [
'DELETE',
returnedVMIDs
], function(err, rows) {
if (err){
reject(err);
}
console.log('finish update');
// dont' need to return anything here
resolve(0);
});
mariasqlClient.end();
});
}
function getListExpVM(){
return new Promise((resolve, reject) => {
var vmList = [];
var mariasqlClient = dbConnection();
mariasqlClient.query( sqlSearch_ByUSERNAMEAndSTATE, [
requesterUsername,
'ACTIVE'
], function(err, rows) {
if (err){
reject(err);
}
vmList = filterExpiredVMs(rows);
var response = {
status : 200,
success : 'Successfull',
data : vmList,
requester: requesterUsername
};
resolve(response);
});
mariasqlClient.end();
});
}
router.post('/processVMs', function(req, res) {
var returnedVMIDs = JSON.parse(req.body.data);
processVMDelete(returnedVMIDs)
.then(res => {
console.log('done');
// check if there is more available for the user:
getListExpVM()
.then(response => {
console.log('sending back list of VMs');
//===>>> ERROR HERE: res.end is not a function
res.end(JSON.stringify(response));
})
.catch(err => {
console.log('error', err.message);
logger.error("Error getting expired VMs: " + err.message);
//===>>> ERROR HERE: res.send is not a function
res.status(500).send({error: err.message})
});
})
.catch(err => {
console.log('error', err.message);
logger.error("Error processing VMs: " + err.message);
//===>>> ERROR HERE: res.send is not a function
res.status(500).send({error: err.message})
});
});
You've redefined res with this:
processVMDelete(returnedVMIDs)
.then(res => {...})
This will hide the higher scoped res associated with the overall request (the one you need to use for res.end()). Change the name of this one to something else like result and then change the corresponding references that use this result.
I'm using a Node.js server to make requests to an Azure sql database.
As far as I understand the following function does not prevent sql injection:
Current code: (working but unsafe)
var executeQuery = async function(query, response) {
const pool = new sql.ConnectionPool(dbConfig)
pool.on('error', err => {
console.log('sql errors', err);
});
try {
await pool.connect();
let result = await pool.request().query(query);
response.send(result.recordset);
return {success: result}
} catch (err) {
return {err: err};
} finally {
console.log('request complete')
pool.close(); // closing connection after request is finished
}
};
app.get("/api/workOrders/byId/:workOrderId", function(req, res) {
console.log(req.params);
var query = "SELECT * FROM [WorkOrder] WHERE [idWorkOrder]=" + req.params.workOrderId;
executeQuery(query, res);
});
I would like to have the executeQuery function standalone, but I did not find an answer for that yet. Anyway, this is the code I constructed from mssql documentation:
New Code (not working)
app.get("/api/test/:workOrderId", function(req, res) {
console.log(req.params.workOrderId);
(async function() {
const pool = new sql.ConnectionPool(dbConfig)
pool.on('error', err => {
console.log('sql errors', err);
});
try {
await pool.connect();
let result = await pool.request()
.input('input_parameter', sql.VarChar(50), req.params.workOrderId)
.query('SELECT * FROM [Quotation] WHERE [idWorkOrder]= #input_parameter');
console.log(result);
res.send(result.recordset);
return {success: result}
} catch (err) {
return {err: err};
} finally {
console.log('request complete')
pool.close(); // closing connection after request is finished
}
});
})
This version should be injection proof, but It does not return anything. Is there an option to pass the input values to the executeQuery function as in the current code?
You can pass the value of req.params.workOrderId into your async function and then use that value inside. check the following code.
app.get("/api/test/:workOrderId", function(req, res) {
console.log(req.params.workOrderId);
(async function(workOrderId) {
const pool = new sql.ConnectionPool(dbConfig)
pool.on('error', err => {
console.log('sql errors', err);
});
try {
await pool.connect();
let result = await pool.request()
.input('input_parameter', sql.VarChar(50), workOrderId)
.query('SELECT * FROM [Quotation] WHERE [idWorkOrder]= #input_parameter');
console.log(result);
res.send(result.recordset);
return {success: result}
} catch (err) {
return {err: err};
} finally {
console.log('request complete')
pool.close(); // closing connection after request is finished
}
})(req.params.workOrderId); // <===pass value to the function
})
I am JS newbie so this may be some silly trouble. I have a lambda written in NodeJS 10.x and I am trying to add MongoDB Atlas insertion. I have started with this tutorial: https://docs.atlas.mongodb.com/best-practices-connecting-to-aws-lambda/
This is my code:
const MongoClient = require('mongodb').MongoClient;
let cachedDb = null;
function connectToDatabase (uri) {
console.log('Connect to mongo database');
if (cachedDb) {
console.log('Using cached database instance');
return Promise.resolve(cachedDb);
}
return MongoClient.connect(uri)
.then(db => {
console.log('Successful connect');
cachedDb = db;
return cachedDb;
}).catch(err => {
console.log('Connection error occurred: ', err);
callback(err);
});
}
function insertUser(db, email) {
console.log('=> modify database');
return db.collection('users').insertOne({"email" : email})
.then(() => { callback(null, result); })
.catch(err => {
console.log('Insert error occurred: ', err);
callback(err);
});
}
exports.handler = (payload, context, callback) => {
const { email, password } = JSON.parse(payload.body);
context.callbackWaitsForEmptyEventLoop = false;
connectToDatabase(MONGODB_URI)
.then(db => {
console.log('Mongo connected')
insertUser(db, email);
})
.then(result => {
console.log('Mongo insert succeeded', result);
})
.catch(err => {
console.log('Mongo insert failed', err);
return responses.INTERNAL_SERVER_ERROR_500(err, callback, response);
});
console.log('finished mongo stuff');
I can see the following logs in CloudWatch:
START RequestId: 0338d336-7d33-40d5-abc7-1511f1c9ea4c Version: $LATEST
2020-01-11T12:18:00.808Z 0338d336-7d33-40d5-abc7-1511f1c9ea4c INFO Connect to mongo database
2020-01-11T12:18:00.855Z 0338d336-7d33-40d5-abc7-1511f1c9ea4c INFO finished mongo stuff
2020-01-11T12:18:01.416Z 0338d336-7d33-40d5-abc7-1511f1c9ea4c ERROR (node:8) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.
END RequestId: 0338d336-7d33-40d5-abc7-1511f1c9ea4c
The item is not inserted in Atlas. I have added more verbose logging but it is not shown. If the mongo connect failed there shall be an error. But it seems that the error handlers are ignored. Where is the problem?
You are very close. Few things are missing
callback function needs to be called from your handler function as the insert function doesn't know about callback.
When you do a .then on a promise and you use curly braces you need to return whatever is in there to send it to the next then (unless you do it in a single line).
db in insert method needs to be db.db()
I would recommend to to use async/await instead of callback style. Here is the equivalent code
exports.handler = async (payload) => {
try {
const { email, password } = JSON.parse(payload.body);
const db = await connectToDatabase(MONGODB_URI);
console.log("Mongo connected");
const result = await insertUser(db, email);
console.log("Mongo insert succeeded", result);
return result;
} catch(err) {
console.error(err);
}
};
I have the following async method:
alreadyLoaded: async function (id) {
const pool = await poolPromise;
return pool.request()
.input('idParameter', id)
.query('SELECT count(*) AS value FROM dbo.partidos WHERE id=#idParameter')
.then(result => {
console.log(result.recordset[0].value)
result.recordset[0].value > 0
}).catch(function(err) {
console.log(err.message);
});
Invoked in another one:
processMatches: function(payload) {
payload.matches.forEach(p => {
if(partidosRepository.alreadyLoaded(p.id))
{
console.log("El partido ya fue cargado.");
return;
}
The alreadyLoaded method checks if some record is already inserted on database, and it's invoked inside another method to validate. The problem here is that the processMatches method continues processing records before the alreadyLoaded finishs with the current one.
I'm having a wrong manage of promises here, Can anyone help me solve this out?
Here's the database connection:
const poolPromise = new sql.ConnectionPool(config)
.connect()
.then(pool => {
console.log('Connected to localhost '+ config.database +' database');
return pool;
})
.catch(err => console.log('Database connection failed. Error: ', err));
module.exports = {
sql, poolPromise
}
Why don't you build your if statement like this
if(await partidosRepository.alreadyLoaded(p.id))
also keep in mind you are not returning boolean value here
}).catch(function(err) {
console.log(err.message);
});