I am calling 2 API request 1 after another so I decided to use the waterfall model but I am facing the issue in it
I have tried so much but not able to solve the issue.
Below is my code:
var unirest = require("unirest");
var async = require("async")
exports.user = (req, res, next) => {
const qry = req.params.id
async.waterfall([
(nextCall) => {
var req = unirest("GET", API_URL1);
req.query({
// some query
});
req.headers({
// some headers
});
req.end(function(subCount) {
// if (resp.error) throw new Error(resp.error);
var channelSubCount = subCount.body
nextCall(null, data)
});
},
(data, nextCall => {
console.log(channelSubCount, 'data')
var reqs = unirest("GET", API_URL2);
reqs.query({
// some query
});
reqs.headers({
// some headers
});
reqs.end(function(res) {
// if (res.error) throw new Error(res.error);
console.log(res.body);
return nextCall(null, {
name: 'abc',
photo: 'src',
count: data
})
});
})
], function(finalData) {
// if (error) { alert('Something is wrong!'); }
console.log('final')
res.status(200).json(
finalData
);
});
};
ERROR:
Reference Error: data is not defined
I don't understand why this is happening.
Also some please show me the right way to implement the above things with optimizations.
Any help appreciated...
Looks like you forgot to close parentheses here in your second arrow function definition:
(data, nextCall => {
It's still a valid JavaScript, but the interpreter now treats data not as a function incoming parameter (as you need), but as a variable. But it's not defined anywhere, therefore you have that error.
Make it like this and it will work:
(data, nextCall) => {
Related
Meteor newbie here, I want to do a meteor call to a meteor method which does a get request to an 3rd party API and forwards the returned JSON response to my initial meteor call.
I want to use the returned JSON response to render my html code
here is my js file with meteor method
Meteor.methods({
'jira.get'(projectName, maxResult) {
if (!this.userId) {
throw new Meteor.Error('Not authorized.');
}
check(projectName, String);
check(maxResult, String);
const base64Auth = Meteor.users.findOne({ _id: this.userId }).Authorization;
const options = {
method: 'GET',
url: `https://myCompany.com/.../search?jql=project=${projectName}&maxResults=${maxResult}`,
headers: {
Authorization: base64Auth,
},
};
const future = new Future();
request(options, function(error, response) {
if (error) throw new Error(error);
const jiraResponse = JSON.parse(response.body);
future.return(jiraResponse);
});
return future.wait();
},
});
and my JSX file that calls above Meteor method is as below
export const App = () => {
Meteor.call('jira.get', project='test', maxResult = '10', (error, jiraResponse) => {
console.log("this is working fine: "jiraResponse)
});
console.log('this is undefined', jiraResponse)
}
If i use useState as below, initially the last console log console.log(jiraResp) prints {} as expected but it goes into infinite loop with the correct data after that
const [ jiraResp, setjiraResp ] = useState({})
Meteor.call('jira.get', project='test', maxResult = '10', (error, jiraResponse) => {
if (jiraResponse){
console.log("got the resp ")
setjiraResp(jiraResponse);
}
else{
console.log("not recieved")
}
});
console.log(jiraResp)
How do i get the response of meteor call and update my jiraResponse just once ?
Setting jiraResp in the method callback will trigger a re-render and since you are making the method call in the render method itself, it will repeat the call, hence the loop.
You need to use useEffect:
const [ jiraResp, setjiraResp ] = useState({});
useEffect(() =>
Meteor.call('jira.get', project = 'test', maxResult = '10', (error, jiraResponse) => {
if (jiraResponse) {
console.log("got the resp ");
setjiraResp(jiraResponse);
} else {
console.log("not recieved");
}
}), []);
console.log(jiraResp);
I have this function which is async and i'm trying to make a simple query from npm-mysql db.
let sortCategory = async (id) => {
try {
var sql = 'SELECT * FROM categories WHERE parent_id=?';
var results = await connection.query(sql, id);
// console.log(results);
return(results);
} catch(err) {
console.log(err);
return false;
}
}
But instead of results inside the results variable i just get the query object.
Query {
_events:
[Object: null prototype] {
error: [Function],
packet: [Function],
timeout: [Function],
end: [Function] },
_eventsCount: 4,
_maxListeners: undefined,
_callback: undefined,
_callSite:
Error
at Protocol._enqueue (C:\Users\fedesc\Sites\borsalino\node_modules\mysql\lib\protocol\Protocol.js:144:48)
at Connection.query (C:\Users\fedesc\Sites\borsalino\node_modules\mysql\lib\Connection.js:198:25)
at sortCategory (C:\Users\fedesc\Sites\borsalino\server\routes\categories.js:35:38)
at router.post (C:\Users\fedesc\Sites\borsalino\server\routes\categories.js:48:31)
at process._tickCallback (internal/process/next_tick.js:68:7),
_ended: false,
_timeout: undefined,
_timer: Timer { _object: [Circular], _timeout: null },
sql: 'SELECT * FROM categories WHERE parent_id=\'0\'',
values: '0',
.... }
The query as seen in object is
sql: 'SELECT * FROM categories WHERE parent_id=\'0\'',
values: '0',
EDIT#1
an async/await for INSERT query does works. it's only when i need to retrieve data back that i don't get it.
but i can't manage to get the results back even though i do have some in table that should return.
i feel like there is something i still not quite understand about mysql and async calls.
thanks guys.
I use async/await of mysql query like this:
var getCategories = function (id) {
return new Promise(function (resolve, reject) {
var sql = `SELECT * FROM categories WHERE parent_id=?`;
connection.query(sql, [id], function (err, result) {
if (!err) {
resolve(result);
} else {
resolve({
status: "error",
message: "Error Getting Data",
debug: err
});
}
});
});
};
try {
var categories = await getCategories();
} catch (error) {
console.log(error);
}
Above code is very different from yours but you can use the above method to use in further case
Thank you for your helpful posts fedesc. I’d been struggling with this for days. Based on your lead, I ended up with this which is elegant relative to my earlier attempts:
'use strict';
const mysql = require('mysql');
const config = require('./config.js');
const util = require('util'); // needed for async support
const ac = awaitableConnection( config );
demoAwait();
async function demoAwait() {
try {
const results1 = await ac.query( 'SELECT * FROM table' );
const results2 = await ac.query( 'SELECT * FROM table WHERE whatever' );
console.log(results1); // all above results are available
// throw 'test error'; uncomment to test an error
} catch ( err ) {
console.log(err);
} finally {
await ac.close();
}
}
function awaitableConnection( config ) { // wrapped in a promise
const connection = mysql.createConnection( config );
return {
query( sql, args ) {
return util.promisify( connection.query )
.call( connection, sql, args );
},
close() {
return util.promisify( connection.end ).call( connection );
}
};
}
The technique remains readable when queries are placed in a loop. I have to acknowledge Michał Męciński for the pattern of this technique. In Sept 2019 he updated the article fedesc linked above while taking advantage of node.js 8 or later. The article also demonstrates how to use a similar technique for transactions. Node.js, MySQL and async/await
As I can see in documentation https://www.npmjs.com/package/mysql
connection.query('SELECT * FROM `books` WHERE `author` = ?', ['David'], function (error, results, fields) {
// error will be an Error if one occurred during the query
// results will contain the results of the query
// fields will contain information about the returned results fields (if any)
});
You code should became
var sql = 'SELECT * FROM categories WHERE parent_id=?';
connection.query(sql, [id], function (error, results, fields) {
if(error){
return error;
}
return results;
});
1st of all thank you kind responders.
The answer of both of you was indeed the same and the correct one. So i just accepted the quickest responder.
NPM Mysql functions do operate in an old school callback style (and needs to be updated) What was really strange for me is that an INSERT statement did work out of the box - I guess this is because you don't really need a callback if you don't need data to be retrieved.
And async/await is part of node and not mysql.
So the call did indeed fired but without a callback.
Connection.prototype.query = function query(sql, values, cb) {
var query = Connection.createQuery(sql, values, cb);
query._connection = this;
if (!(typeof sql === 'object' && 'typeCast' in sql)) {
query.typeCast = this.config.typeCast;
}
if (query.sql) {
query.sql = this.format(query.sql, query.values);
}
if (query._callback) {
query._callback = wrapCallbackInDomain(this, query._callback);
}
this._implyConnect();
return this._protocol._enqueue(query);
};
Therefore your answers are accurate and correct.
Allow me to elaborate on a possible solution i found for my problem with the hope that maybe it'll help readers who face this approach issue as well.
There is a workaround i've found when still searching for a solution here - How to use classes in Node.js (with no pre-compilers), and why you should and here - Node.js, MySQL and promises
The solution was "promisifying" mysql functions with a class alike function that converts all mysql functions to promises.
Which than will give the option to work with database in an async/await approach.
Also there are tools that promisify functions that uses callbacks like this one here
//mysql
const mysql = require('mysql');
function Database() {
this.connection = mysql.createConnection({
host : 'localhost',
user : '*************',
password : '*************',
database : '*************',
multipleStatements: true
});
this.query = (sql, args) => {
return new Promise((resolve, reject) => {
this.connection.query(sql, args, (err, rows) => {
if (err)
return reject(err);
resolve(rows);
});
});
};
this.close = () => {
return async () => {
try {
this.connection.end(err => {
if (err) throw err;
return;
});
} catch(e) {
return e;
}
}
};
};
var connection = new Database();
Setting you db connection this way will allow you now to use async/await as in original question.
let sortCategory = async (id) => {
try {
var sql = 'SELECT * FROM categories WHERE parent_id=?';
var results = await connection.query(sql, id);
// console.log(results);
return results;
} catch(err) {
console.log(err);
return false;
}
}
Hope this helps anyone.
I have inherited the following code. This is part of CICD pipeline. It tries to get an object called "changes" from a bucket and does something with it. If it is able to grab the object, it sends a success message back to pipeline. If it fails to grab the file for whatever reason, it sends a failure message back to codepipeline.
This "changes" file is made in previous step of the codepipeline. However, sometimes it is valid for this file NOT to exist (i.e. when there IS no change).
Currently, the following code makes no distinction if file simply does not exist OR some reason code failed to get it (access denied etc.)
Desired:
I would like to send a success message back to codepipeline if file is simply not there.
If there is access issue , then the current outcome of "failure' would still be valid.
Any help is greatly appreciated. Unfortunately I am not good enough with Javascript to have any ideas to try.
RELEVANT PARTS OF THE CODE
const AWS = require("aws-sdk");
const s3 = new AWS.S3();
const lambda = new AWS.Lambda();
const codePipeline = new AWS.CodePipeline();
// GET THESE FROM ENV Variables
const {
API_SOURCE_S3_BUCKET: s3Bucket,
ENV: env
} = process.env;
const jobSuccess = (CodePipeline, params) => {
return new Promise((resolve, reject) => {
CodePipeline.putJobSuccessResult(params, (err, data) => {
if (err) { reject(err); }
else { resolve(data); }
});
});
};
const jobFailure = (CodePipeline, params) => {
return new Promise((resolve, reject) => {
CodePipeline.putJobFailureResult(params, (err, data) => {
if (err) { reject(err); }
else { resolve(data); }
});
});
};
// MAIN CALLER FUNCTION. STARTING POINT
exports.handler = async (event, context, callback) => {
try {
// WHAT IS IN changes file in S3
let changesFile = await getObject(s3, s3Bucket, `lambda/${version}/changes`);
let changes = changesFile.trim().split("\n");
console.log("List of Changes");
console.log(changes);
let params = { jobId };
let jobSuccessResponse = await jobSuccess(codePipeline, params);
context.succeed("Job Success");
}
catch (exception) {
let message = "Job Failure (General)";
let failureParams = {
jobId,
failureDetails: {
message: JSON.stringify(message),
type: "JobFailed",
externalExecutionId: context.invokeid
}
};
let jobFailureResponse = await jobFailure(codePipeline, failureParams);
console.log(message, exception);
context.fail(`${message}: ${exception}`);
}
};
S3 should return an error code in the exception:
The ones you care about are below:
AccessDenied - Access Denied
NoSuchKey - The specified key does not exist.
So in your catch block you should be able to validate exception.code to check if it matches one of these 2.
I'm trying to setup a NodeJS application with GraphiQL API and MySQL database connection. All of it seem to work until I'm trying to get the data that was fetched from the database be available for GraphQL to be able to do something with it.
Here we have the app.js file, which is the starting point of the backend. Assume that all the imports and declarations are valid.
app.use('/api', graphql({
schema: buildSchema(schema),
rootValue: resolvers,
graphiql: true
}));
The rootValue is as follows.
const resolvers = {
regions: () => {
var a = manager.getRegions();
console.log("a: " + a);
return a;
}
};
The manager object. I have the manager object incase I want to change database type in the future.
const manager = {
getRegions : function() {
console.log("getRegions entered...");
return processQuery("regions");
}
};
Finally, under the MySQL script we have.
const processQuery = (query) => {
var res = null;
switch (query) {
case 'regions':
default:
db.query(SELECT_REGIONS_QUERY, (err, rows) => {
if (err) {
throw err;
} else {
res = JSON.stringify(rows);
console.log("Stringify: " + res);
}
});
}
return res;
}
I've read numerous pages and even stackoverflow posts about Promise, callback functions and async/await but neither (atleast to code attempts made by me) seem to make the printout under the rootValue be printed last...
I saw an implementation done by Academind that uses MongoDB instead and he doesn't seem to have to care about this issue. Any ideas on how to solve this? Thank you!
What you can to is make the processQuery an asynchronous function and just wait for the db.query to be solved.
const processQuery = async (query) => {
var res = null;
switch (query) {
case 'regions':
default:
await db.query(SELECT_REGIONS_QUERY, (err, rows) => {
if (err) {
throw err;
} else {
res = JSON.stringify(rows);
console.log("Stringify: " + res);
}
});
}
return res;
}
I'm using Express and trying to teach myself node/javascript callbacks and I've stumbled across something.
I have a route that looks like this:
var express = require('express');
var router = express.Router();
var api = require('../api');
router.get('/',function(req, res, next){
var modulename = api.modulename;
modulename.methodname(res);
});
module.exports = router;
And then the module that is being called above looks like this:
var library = require('library');
var instances = {};
var modulename = {
getAllInstances: function(res) {
var request = new library.asyncMethod();
request.on('success', function(resp) {
instances = resp.data;
res.setHeader("Content-Type","application/json");
var returnInstances = {
id: instances[0].InstanceId,
state: {name: instances[0].State.Name, code: instances[0].State.Code}
};
res.send(returnInstances);
})
.on('error', function(resp){
console.log(resp);
})
}
};
module.exports = modulename;
As you can see I'm passing through the response parameter through to my module, but I'd rather pass back instances and then in the route return api.modulename.instances, like this:
var library = require('library');
var instances = {};
var modulename = {
getAllInstances: function() {
var request = new library.asyncMethod();
request.on('success', function(resp) {
var returnData = resp.data;
instances = {
id: returnData[0].InstanceId,
state: {name: returnData[0].State.Name, code: returnData[0].State.Code}
};
})
.on('error', function(resp){
console.log(resp);
})
.send();
}
};
module.exports = modulename;
However, when I do, it's coming through as the default value {} but if I run it as above, I do get output so I know that there should be data in there.
Let me know if I have misunderstood your issue. If you are saying you want to pass back objects from getAllInstances then you pass in a callback and call it from the event handler like this-
router.get('/',function(req, res, next){
var modulename = api.modulename;
modulename.getAllInstances(res, function(err, instances){
if(err){ ... }
else{
res.send(instances); //or however you want to use instances
}
});
});
and in getInstances
var modulename = {
getAllInstances: function(res, cb) {
var request = new library.asyncMethod();
request.on('success', function(resp) {
instances = resp.data;
var returnInstances = {
id: instances[0].InstanceId,
state: {name: instances[0].State.Name, code: instances[0].State.Code}
};
cb(null, instances);
})
.on('error', function(err){
cb(err, null));
});
//.send(); not sure what this is it seems to be request.send() ??
}
};
The problem here lies with when the response from the API call is available. The event loop in Node means code won't block until the API replies with a response. Hence a callback is needed to handle that response when it becomes available. You probably want to use the API response in your Express router response so there's a chain of dependency.
One strategy here would be to use promises and not callbacks, it would alleviate some of the pain you're experiencing with async response from the API call.
In your routes:
router.get('/',function(req, res, next){
var instances = [];
// The function below could be refactored into a library to minimise controller code.
var resolver = function (response) {
var data = JSON.parse(response);
instances.push({
name: data[0].State.Name,
code: data[0].State.Code
});
res.render('instances'. {instances : instances});
};
modulename.methodname(resolver);
});
And in your module:
var rp = require('request-promise'); // Also see q-io/http as an alternate lib.
var modulename = {
methodname: function (resolver) {
rp('http://the-inter.net')
.then(resolver)
.catch(console.error);
}
};
This might not cut-n-paste work but have a look at the request-promise examples for further clarification.