I'm trying to figure out the difference between next() and ctx.end() in connector hooks of loopback.
Moreover, why the ctx.end() function cannot be used for other hooks?
Thanks for your help !
Because they are the same if the end function not defined.
There is some piece of code in loopback datasource juggler :
if (context.end === undefined) {
context.end = callback;
}
You can check it out on this line
So it's the same that which one you use.
After digging a while :
First, next() and ctx.end() do share the same signature.
If several observers are bound to the connector, they are called one after the other (by order of binding).
Calling next() calls the next observer
Calling ctx.end() ends "closes" the event
Example-1 (coffee) :
connectorName.connector.observe 'after execute', (ctx, next) ->
console.log "hook1", new Date()
wait(3000)
ctx.end null, ctx, ctx.res.body
connectorName.connector.observe 'after execute', (ctx, next) ->
console.log "hook2", new Date()
wait(3000)
next()
Example-2 (coffee) :
connectorName.connector.observe 'after execute', (ctx, next) ->
console.log "hook1", new Date()
wait(3000);
next null, ctx, ctx.res.body
connectorName.connector.observe 'after execute', (ctx, next) ->
console.log "hook2", new Date()
wait(3000)
next()
In the first example, the second hook is never called. In the second it is normally called.
pgConnector.connector.observe 'after execute', (ctx, next) ->
console.log "hook2", new Date()
wait(3000);
ctx.end(null, ctx.res)
pgConnector.connector.observe 'after execute', (ctx, next) ->
console.log "hook3", new Date()
wait(3000);
next()
Same here with database connector.
Related
Im trying to call a function exported from another file inside a async each loop in which it iterates over an array of incoming data and executes queries accordingly like this :
const query = require('./queries')
function receive(req,resp ,doneFunc){
const listData = [];
let checkedList = req.body.checkedList
async.each(checkedList, (item, next)=>{
//Every iteration gets a parameter called action from an object inside the array which is the
//name of the function needed
//
query[item.action](req, res, resp, (err, data)=>{
listData.push(data);
if(listData.length === checkedList.length)doneFunc(listData);
next();
});
}, err=>{
console.log(err);
});
}
The function im calling in query[item.action] has the following structure
exports.any = function(res,callback){
MongoClient.connect(url,function (err, client) {
let dbo = client.db("DB")
if(!err) {
dbo.collection("collection",function(err ,coll){
coll.aggregate([
//make aggregation
]).toArray(function(err,data){
//execute callback when the aggregation has finished , this is where the error ocurrs
if(!err) return callback(null,data)
return callback(err,null)
})
})
} else {
return callback(err,null);
}
});
}
When the execution of the async.each loop reaches the call for query it returns the message
TypeError: callback is not a function
at ...\server\routes\queries.js:385:37
Which is where the return callback(null,data) is supposed to be executed.
What is the reason of this error , is the function set wrong or is it executed in a wrong way?
If I have understood your question (despite a lot of code missing to understand the question), this is the mistake:
function(res,callback){ // function signature
query[item.action](req, res, resp, (err, data)=>{ // function call
They do not match at all, you are passing res as callback and inside function you are using callback (which actually is res) as a function.
I am trying to create a MEAN stack app, I'm currently working on the UPDATE functionality.
My code is currently failing when it runs into this method:
businessRoutes.route('/update/:id').post(function (req, res) {
Business.findById(req.params.id, function (err, business) {
if (!business)
return next(new Error('Could not load Document'));
else {
business.person_name = req.body.person_name;
business.business_name = req.body.business_name;
business.business_gst_number = req.body.business_gst_number;
business.save().then(business => {
res.json('Update complete');
console.log('Update Complete');
})
.catch(err => {
res.status(400).send("unable to update the database");
});
}
});
});
The error message being displayed in the console is:
TypeError: next is not a function
It's failing on this line of code:
return next(new Error('Could not load Document'));
Can someone please tell me why this is occurring & how I can resolve it?
The second parameter to findById expects a callback that has two arguments, err and <entity>. There's no middleware or something else in place, what you call next(...) tries to call your found entity.
From the docs
Adventure.findById(id, function (err, adventure) {});
You see, in your case, business is always undefined, and adventure or next is never a function.
Here is what you probably meant:
The problem is that the param that you mean to be "next" actually comes from express.js (which means it comes in on the first function call back that you ran (after .post(---this function--- has 3 params that you may chose to use:
req the request that the user made to your server
res the response that you are getting ready to send
next the 3rd param is optional and allows you to send to the next middleware or if you send it a parameter it will send it to the error handler which will send your error as a response.
On the other hand:
you placed the next param randomly in the middle of the mongoose function that you attempted to call: the mongoose function actually only takes (err, item)...
businessRoutes.route('/update/:id').post(function (req, res, next) {
// note that express exposes the "next" param
// this is a callback indicating to run the `next` middleware
Business.findById(req.params.id, function (err, business, whoKnows) {
// note this is from mongoose.js: the callback function actually only has 2 parameters (err, and foundObject)
if (!business)
return next(new Error('Could not load Document'));
else {
business.person_name = req.body.person_name;
business.business_name = req.body.business_name;
business.business_gst_number = req.body.business_gst_number;
business.save().then(business => {
res.json('Update complete');
})
.catch(err => {
res.status(400).send("unable to update the database");
});
}
});
});
Be advised, that I'd recommend using async await and that would make the whole thing much easier to understand:
businessRoutes.route('/update/:id').post(async (req, res, next) => {
try {
const foundBusiness = await Business.findById(req.params.id)
//... do stuff
catch (e) {
next(throw new Error(e))
// .. other stuff
}
})
I'm stuck on what the best way to create a wrapper callback function in node/express that reuses the same parameters.
The thing is that the verification requires the same parameters the callback does. Is there a way to simplify this? I've looked for this on stack and google, but couldn't find an answer. I don't want to write req, res, next twice in the actual call. I understand the first req, res, next are being funneled into the callback, but it still feels weird to write it like this. It feels like there's definitely a better approach, but I just don't know what that is.
Here's the situation:
function verifyCaptcha(req, res, next, callback) {
let captchaResponse = req.body.captchaResponse;
let captchaSecret = "it's a secret";
let captchaURL = "https://www.google.com/recaptcha/api/siteverify?"
+ "secret=" + encodeURIComponent(captchaSecret) + "&"
+ "response=" + encodeURIComponent(captchaResponse) + "&"
+ "remoteip" + encodeURIComponent(req.connection.remoteAddress);
// request is ASYNC, that's why I need the callback
request(captchaURL, function(error, response, body) {
callback(req, res, next);
return;
});
};
router.post('/login', function(req, res, next) {
// example call INSIDE a route (here's the problem b/c params are repeated in the call
verifyCaptcha(req, res, next, function(req, res, next){
// do stuff
});
};
Promises are supposed to avoid callback hell. All popular callback-based libraries have promisified counterparts, it's request-promise for request. This can be written in sync-like manner with async..await:
const request = require('request-promise');
async function verifyCaptcha(req, res) {
let captchaResponse = req.body.captchaResponse;
let captchaSecret = "it's a secret";
let captchaURL = "https://www.google.com/recaptcha/api/siteverify?"
+ "secret=" + encodeURIComponent(captchaSecret) + "&"
+ "response=" + encodeURIComponent(captchaResponse) + "&"
+ "remoteip" + encodeURIComponent(req.connection.remoteAddress);
const result = await request(captchaURL);
...
};
router.post('/login', async function(req, res, next) {
try {
await verifyCaptcha(req, res);
} catch (err) {
next(err);
}
};
As explained in this question, Express doesn't support promises natively, all rejections should be handled by a developer, async middleware/handler function body should be wrapped with try..catch.
According to the express documents, you can chain middleware by simply adding it to the route call.
http://expressjs.com/en/4x/api.html#app.use
which allows you to do this:
function verifyCaptcha(req, res, next) {
let captchaUrl = ...
request(captchaUrl, function(error, response, body) {
if (error) res.sendStatus(403)
next()
}
}
router.post('/login', verifyCaptcha, function(req, res, next) {
// this will only run if verifyCaptcha calls next()
}
The end result is a lot more readable in practice.
First, I'm sorry for the title, I couldn't mind up something better.
I thought I understand Node.js / KOA, at least the basics but now I'm starting to feel that I'm missing some fundamentals.
Take a look at the following code:
router.put("/",
parse,
async function (ctx, next) {
// do something
await next();
},
async function (ctx, next) {
// do something
await next();
},
async function (ctx, next) {
if (some condition) {
gm(imageBuffer)
.quality(80)
.write(profile_path, async function (err) {
gm(imageBuffer)
.resize(60, 60)
.quality(80)
.write(chat_path,async function (err) {
await next(); // HERE 1
});
});
} else {
await next();
}
// HERE 2
},
async function (ctx, next) {
responses.success(ctx, "Success");
}
);
So what this is all about. The ones that are familiar with KOA framework will immediately see what is going on here. Where my problem starts/ends is in the third async function. So what I'm trying to do here is some image manipulation (saving). gm is asynchronus, but as you can see from the code I'm using anonymous callback functions, and what I'm trying to achieve is that the last async function is being called when gm finishes through await next(); // HERE 1.
But what really happens is (from my understanding)... gm starts asynchronously.. and // HERE 2 is hit, and because there's nothing, end of function, KOA returns default 404 response. I simply can't understand why this is so and how to overcome this.
What I really want to happen is when callback finishes await next(); // HERE 1 gets called and I can return success response.
await next(); // HERE 1 of course gets called (eventually) but too late, because KOA already responds with 404.
If there is someone that is able and willing to explain what exactly is happening here, thank you.
As far as I see is that your aproach is not really following the async await pattern: The async function should the "await" the asynchronous part. This then needs to return a promise. So you have to encapulate your callback in a promise. Something like this could work (not testet, just to show the concept):
function gmAsync(){
return new Promise(function(resolve,reject){
gm(imageBuffer)
.quality(80)
.write(profile_path, async function (err) {
gm(imageBuffer)
.resize(60, 60)
.quality(80)
.write(chat_path,async function (err) {
resolve(....whatever....);
});
});
});
}
and then you async function could look like this:
async function (ctx, next) {
if (some condition) {
await gmAsync()
next()
} else {
await next();
}
},
...
Makes sense?
This may be extremely stupid, but I haven't found much about this because I don't understand how I should search this.
I have a route handler that may call different functions depending on some request parameters, and I would like to know what's the best way to deal with errors inside the functions in order to pass errors to the error handling middleware.
Consider something like this:
router.get('/error/:error_id', (req, res, next) => {
my_function();
}
function my_function(){
// do something async, like readfile
var f = fs.readFile("blablabla", function (err, data) {
// would want to deal with the error
});
}
If an error occurs during fs.readFile, how do I pass the error to next to forward it to the error middleware? The only solution is to pass the next param to the function function my_function(next){...}?
In case the function didn't call any async I/O operation, a simple try/catch in the route handler would be ok (i suppose), like this:
router.get('/error/:error_id', (req, res, next) => {
try{
my_function();
} catch(e){
next(e);
};
}
function my_function(){
// do stuff
var f = fs.readFileSync("blablabla"); // possibly throws an error
}
Hope I make some sense.
You are totally correct that you should pass the next callback to my_function since fs.readFile is asynchronous.
router.get('/error/:error_id', (req, res, next) => {
my_function(next);
}
function my_function(next) {
fs.readFile("blablabla", function (err, data) {
if (err) {
next(err);
} else {
// Process the data
// Don't forget to call `next` to send respond the client
}
});
}
By the way, you cannot do
var f = fs.readFile(...)
because fs.readFile is asynchronous. The data should be handled within the callback.