Export a function from a function - node.js

I have an ExpressJS app where I have api.js in routes that manages connecting to Couchbase and then emits event couchbaseConnected that is awaited by init() function inside api.js.
Inside init() I want to push those exports.someFunction(req, res){return something;}. But when I just put these exports inside init() function, I get an error .get() requires callback functions but got a [object Undefined] so it seems like I am doing it wrong.
The question is how I can export functions from another function in NodeJS?
Here is the code:
//connecting to couchbase and emitting event on connection
couchbase.connect(dbConfiguration, function (err, bucket) {
if (err) {
console.log(err);
}
cb = bucket;
eventEmitter.emit('couchbaseConnected');
});
//listening for the event and fire init() when it's there
eventEmitter.on('couchbaseConnected', function (e) {
console.log('Connected to Couchbase.'.green);
init();
});
function init() {
exports.getUserData = function (req, res) {
if (req.user != undefined && req.user.meta != undefined) {
res.json(200, {result: 'ok'})
}
else {
res.json(401, {error: 'Unauthorized request.'})
}
};
}
Here is the ExpressJS .get() that is located in app.js:
app.get('/api/user/data/:type', api.getUserData);

Here is the ExpressJS .get() that is located in app.js:
app.get('/api/user/data/:type', api.getUserData);
The error message .get() requires callback functions but got a [object Undefined] makes quite clear what happens: You require the API module, it starts to connect to the db, you are defining your express app by passing a non-existent property to .get - which fails, since init has not yet been called and getUserData has not yet been assigned. What you need to do is
var api = require('api');
eventEmitter.on('couchbaseConnected', function () {
app.get('/api/user/data/:type', api.getUserData); // now it is available
});
However, this does not look like good code. Instead of loosely coupling them via that couchbaseConnected event you better should use explicit callbacks that are invoked with the requested values (i.e. the cb bucket, or the getUserData method). At least pass them as parameters to the emitted event.
Also, your setup is unconventional. I don't see why getUserData would need to be asynchronously defined - it should always be available. If the couchbase connection failed, I would not expect the /api/user/data/ service to not exist, but to respond with some 500 internal server error message.

This is an answer that I have made some assumptions as the data provided by you is not sufficient to know what you are doing when you start your app.
I would suggest some change in code:
module.exports.connect = function(callback){
couchbase.connect(dbConfiguration, function (err, bucket) {
if (err) {
console.log(err);
callback(err);
}
cb = bucket;
module.exports.getUserData = getUserData();
callback(err); //just callback with no error as you don't require to send the database
});
}
function getUserData(req, res){
if (req.user != undefined && req.user.meta != undefined) {
res.json(200, {result: 'ok'})
}
else {
res.json(401, {error: 'Unauthorized request.'})
}
};
and in your app.js file where you are starting the app just do this
var api = require('./api');
api.connect(function(error){
if (error) throw error;
app.listen(3000);
console.log('Express started on port 3000');
});
And then you can continue doing exactly what you were doing. It should work.
I have assumed that you are not explicitly calling the connect or its equivalent when you are starting the app.....So your getting that error.

Related

nodejs async middlewares is not working

I am trying to make an OAuth authentication flow myself in nodejs, express, and mongodb. I am trying to check if the client is available in my MongoDB before each route. So I made a middleware function that checks if client id and client secret are available in my DB. Here is my middleware function
module.exports = function() {
return function(req, res, next) {
// Implement the middleware function based on the options object
winston.info("check client")
User.getClient(req.get("clientId"), req.get("clientSecret"), function (err, client) {
winston.info("checking time");
if (client) {
return next();
} else {
}
});
winston.info("check client done")
}
}
But as i am calling my mongoose model i think i middleware isnt working perfectly. By the time async call finishes. script is passing through.
So what i want to achieve here is that the middleware should pass if the client is available in our db. So middleware has to go forward after it gets response from the mongoose. Here is my router code.
var express = require('express')
, router = express.Router()
, clientVerify = require("../middlewares/clientVerify");
router.use(clientVerify())
I used promise to solve the issue. Thanks to #Vassilis Pallas for his guideline in the comments.
return new Promise( function(resolve,reject){
User.getClient(req.get("clientId"), req.get("clientSecret"), function (err, client) {
winston.info(client);
if (client) {
resolve( client );
} else {
res.status(401).send({
status: 401,
message: "unauthorized"
});
}
});
}).then(function(saveResult){
saveResult = saveResult && typeof saveResult == 'object' ? saveResult.toJSON() : saveResult;
winston.info(saveResult);
if (saveResult)
return next();
});

Passing data from vendor API to client side for processing

I have a bog standard nodejs and express app. I then have a 3rd party API call (https://github.com/agilecrm/nodejs) that has a set function to collect the data I require. Normally, with an DB call, I am fine, where I call the data return it via res.json(data) and and its available client side in the public folder from express, but I seem to really be struggling with the format of the 3rd party function to get the data to return so I can collect it client side.
Here is an example of the api call:
var AgileCRMManager = require("./agilecrm.js");
var obj = new AgileCRMManager("DOMAIN", "KEY", "EMAIL");
var success = function (data) {
console.log(data);
};
var error = function (data) {
console.log(data);
};
obj.contactAPI.getContactsByTagFilter('tester tag',success, error);
This works fine to console the data, but I need to get it client side so I can use it in the front end, and the only method I know is via routing, how would I achieve this, or is there a better method? Its the fact where the data runs via the 2nd element in the function, that I can't get in my response in the various methods I have tried.
app.get('/get_contacts_by_tag', function (req, res) {
obj.contactAPI.getContactsByTagFilter('Confirmed', success, error);
var success = function (data) {
res.json(data);
};
});
Any help would be greatly appreciated.
You didn't define the error callback and also you assign the success callback after the api call.
app.get('/get_contacts_by_tag', function (req, res) {
var success = function (data) {
res.json(data);
};
var error = function (data) {
res.status(500).json(data);
};
obj.contactAPI.getContactsByTagFilter('Confirmed', success, error);
});

Define/Use a promise in Express POST route on node.js

I currently have a POST route defined in an Express Node.js application as so:
var locationService = require("../app/modules/locationservice.js");
app.post('/createstop', isLoggedIn, function(req, res) {
locationService.createStop(res, req.body);
});
(for this question, please assume the routing in & db works.. my record is created on form submission, it's the response I am struggling with)
In the locationservice.js class I then currently have
var models = require('../models');
exports.createStop = function(res, formData) {
models.location.build({ name: formData.name })
.save()
.then(function(locationObj) {
res.json({ dbResult : locationObj });
});
};
So as you can see, my route invokes the exported function CreateStop which uses the Sequelize persistent layer to insert a record asynchronously, after which I can stick the result on the response in the promised then()
So at the moment this only works by passing the response object into the locationservice.js method and then setting res.json in the then() there. This is sub-optimal to me with regards to my service classes, and doesn't feel right either.
What I would like to be able to do is "treat" my createStop method as a promise/with a callback so I can just return the new location object (or an error) and deal with it in the calling method - as future uses of this method might have a response context/parameter to pass in/be populated.
Therefore in the route I would do something more like:
var locationService = require("../app/modules/locationservice.js");
app.post('/createstop', isLoggedIn, function(req, res) {
locationService.createStop(req.body)
.then(dataBack) {
res.json(dataBack);
};
});
Which means, I could call createStop from else where in the future and react to the response in that promise handler. But this is currently beyond me. I have done my due diligence research, but some individual expert input on my specific case would be most appreciated.
Your locationservice.js could look like that
exports.createShop = function(data){
// here I have used create instead of build -> save
return models.location.create(data).then(function(location){
// here you return instance of saved location
return location;
});
}
And then your post() method should be like below
app.post('/createstop', isLoggedIn, function(req, res){
locationService.createShop(req.body).then(function(location){
// here you access the location created and saved in createShop function
res.json(location);
}).catch(function(error){
// handle the error
});
});
Wrap your createStop function with a promise like so:
exports.createStop = function(res, formData) {
return new Promise(function(resolve, reject) {
models.location.build({ name: formData.name })
.save()
.then(function(locationObj) {
resolve({ dbResult : locationObj });
});
//in case of error, call reject();
});
};
This will allow you to use the .then after the createStop within your router.

Node js make test stub for method chaining

My function is sendMail i want to stub function mailjet and it has a method chain mailjet.post('send').request...
I want to assert callback is called on mail success or fail.
So how i stub this method chain?
var sendMail = function (templateName, callback) {
// From template name find template id of mailjet
mailingExternalTemplateModel.findMailingTemplateId(templateName, function (err, result) {
const request = mailjet
.post("send")
.request(params)
request
.then((result) => {
if (typeof callback === 'function') {
callback(null, result.body);
}
})
.catch((err) => {
if (typeof callback === 'function') {
callback(err, null);
}
})
} else {
callback(err, null);
}
});};
I have done
sinon.stub(mailjet, 'post').withArgs('send').returns(mailjetClient);
sinon.stub(mailjetClient, 'request').returns(Promise);
But i got error TypeError: Attempted to wrap undefined property request as function
I'm the developer in charge of each Mailjet Wrappers, including the NodeJS one.
I'm actually updating each of them and adding features such as the possibility to make the call (or not). For the NodeJS version, a beta will be deployed on npm by tomorrow evening.
I'll update this answer with the modifications you'll have to make (which are few) once the beta is available.
If you are curious, you can still take a look at the modifications I made : https://github.com/mailjet/mailjet-apiv3-nodejs/pull/21

Express.js - display a custom 404 page when a record is not found in MongoDB

I am using node-mongodb-native driver. I tried
collection.findOne({email: 'a#mail.com'}, function(err, result) {
if (!result) throw new Error('Record not found!');
});
But the error is caught by mongodb driver and the express server is terminated.
What's the correct way for this case?
=== Edit===
I have the code below in app.js
app.configure('development', function() {
app.use(express.errorHandler({dumpExceptions: true, showStack: true}));
});
app.configure('production', function() {
app.use(express.errorHandler());
});
Related code in node_modules/mongodb/lib/mongodb/connection/server.js
connectionPool.on("message", function(message) {
try {
......
} catch (err) {
// Throw error in next tick
process.nextTick(function() {
throw err; // <-- here throws an uncaught error
})
}
});
The correct use is not to throw an error, but to pass it to next function. First you define the error handler:
app.error(function (err, req, res, next) {
res.render('error_page.jade');
})
(What's this talk about error being depracated? I don't know anything about that. But even if then you can just use use. The mechanism is still the same.).
Now in your route you pass the error to the handler like this:
function handler(req, res, next) {
collection.findOne({email: 'a#mail.com'}, function(err, result) {
if (!result) {
var myerr = new Error('Record not found!');
return next(myerr); // <---- pass it, not throw it
}
res.render('results.jade', { results: result });
});
};
Make sure that no other code (related to the response) is fired after next(myerr); (that's why I used return there).
Side note: Errors thrown in asynchronous operations are not handled well by Express (well, actually they somewhat are, but that's not what you need). This may crash your app. The only way to capture them is by using
process.on('uncaughtException', function(err) {
// handle it here, log or something
});
but this is a global exception handler, i.e. you cannot use it to send the response to the user.
I'm guessing that the error is not caught. Are you using an Express error handler? Something like:
app.error(function (err, req, res, next) {
res.render('error-page', {
status: 404
});
More on error handling in Express: http://expressjs.com/guide.html#error-handling
In terms of checking for errors off mongodb, use '!error' for success as opposed to '!result' for errors.
collection.findOne({email: 'a#mail.com'}, function(err, result) {
if (!error) {
// do good stuff;
} else {
throw new Error('Record not found!');
}
});
As for the custom 404, I've yet to do that in node and express, but I would imagine it would involve "app.router".

Resources