Controller is not able to pass the response to the index.js - node.js

I am new to this. There are a couple of solutions posted for a similar problem but none of them helped.
I have posted below a very simple toy example that will help you to debug.
index.js
const express = require('express')
port=3001
var controller = require('./controller');
app.use('/api', controller);
controller.js
var model = require('./model')
var router = express.Router();
router.get('/bl', function(req, res) {
model.getData( function (err, objects) {
if(err) return res.send(err);
return res.status(200).json(objects);
});
});
module.exports = router;
model.js
const bl = {"hello":"world"}
const getData= (request, response) => {
return(bl);
}
module.exports = {
getData
}
Issue:
invoking : http://localhost:3001/api/bl => no response
, console : no error
Note:
in my model.js, I am querying in the Postgres database, and I can see the results in console.log.
but I don't see any such result when I try to see data using console.log in controller.js. Similar behavior I observed in the above toy example

I can see an error and something worrying me.
The error is in following two lines
// In controller.js
model.getData( function (err, objects) {
// In models.js
const getData= (request, response) => {
You define a function accepting two arguments (request, response) (both of them are objects) and than you call it passing only one argument of type function.
The thing worrying me is the getData function itself.
const bl = {"hello":"world"}
const getData= (request, response) => {
return(bl);
}
Apart from parameters, it is a sync function, but in your question you pointed that in models.js you query in the Postgres database, and you can see the results in console.log; now you give us no details about how you query Postgress, but I suppose you are doing it with something asynchronous (probably pg). The other relevant detail you didn't revealed is if you query Postgress inside or outside the body of getData function. I hope I'm wrong, but since in your example you are returning something define outside the body of the function, I have the doubt.
Let's consider only the good option, you query Postgress inside the body of the function, probably your complete getData function looks like follows.
const getData = (request, response) => {
client.query('SELECT $1::text as message', ['Hello world!'], (err, res) => {
// Here you get your message in console
console.log(err ? err.stack : res.rows[0].message);
client.end();
})
return something;
// I don't know what, but fore sure something wrong
}
To solve both the problems you just need to fix getData (by chance or by cut&paste you are calling it in the right way in controller.js).
Let's start fixing its signature makeing it accept only one argument: the callback function:
const getData = (done) => {
then let's asynchronously "return" the data we got from Postgress through the done callback function.
const getData = (done) => {
client.query('SELECT $1::text as message', ['Hello world!'], (err, res) => {
// We can leave this for debugging purposes
console.log(err ? err.stack : res.rows[0].message);
// Here we are not handling errors from client.end()...
// but we can neglect about this right now
client.end();
// Let's calle the callback function passing it the result
done(err, res);
})
// There's nothing to return
}
It should be enough.
Hope this helps.

You need a third argument, you will mostly see callback or just cb and execute it in the function and pass some data to it. The first parameter false will be the error argument later and bl will be the objects argument that is passed
const getData= (request, response, callback) => {
callback(false, bl);
}
and then pass req and res arguments to it:
model.getData(req, res, function (err, objects) {
if(err) return res.send(err);
return res.status(200).json(objects);
});
Or the modern way to do it with async / await you could return an promise
const getData= (request, response) => {
return new Promise((resolve, reject) => {
if(someErrorAppear){
reject("some error occured");
}
resolve(bl);
})
}
Now you can go with async / await
router.get('/bl', async function(req, res) {
try {
let objects = await model.getData(req, res);
res.status(200).json(objects);
}catch(err){
res.send(err);
}
});
});

There is a simple solution to this.
Actually, the error is in model.js. Try below code:
const bl = {"hello":"world"}
const getData= (callback) => {
callback (bl)
}
module.exports = {
getData
}

Related

Calling a function within req,res

I have the following code snippet in my routes:
router.get("/home", (req,res) =>{
var message = req.session.user1;
res.render("home", {data: message});
})
In my taskController.js, I have a function that I would query data using Sequelize that should also be passed to home. How can I insert the function in the snippet above?
My initial idea was
router.get("/home", (req,res) =>{
Some query here stored in var task
var message = req.session.user1;
res.render("home", {data: message, dataTwo: task});
})
But this only applies if I do the usual MySql query within the route. But I'm currently using Sequelize (which is in my taskController.js) to do the querying for me. So my option is to call that function in that controller and use it in the router.get above but I'm not quite sure on how to incorporate it.
You can use async/await to asynchronously fetch the task from your controller. Require your controller, then make the function for the route async (req, res), and call the function on your controller that returns the task using await. The function in your controller should also be async and either return the sequelize Promise directly, or after modifying the data as needed.
// import your controller
const controller = require('./controllers/controller');
// make the function async
router.get("/home", async (req, res) => {
// await the result of a function that is async (returns a promise)
const task = await controller.getTask(maybeSomeId);
const message = req.session.user1;
res.render("home", {data: message, dataTwo: task});
});
// you can add error handling like this
router.get("/home2", async (req, res, next) => {
try {
// await the result of a function that is async (returns a promise)
const task = await controller.getTask(maybeSomeId);
const message = req.session.user1;
return res.render("home", {data: message, dataTwo: task});
} catch (err) {
return next(err);
}
});

Problem with getting data from database, "render" function error

I am quite new to express and I created a database in Postgres to extract the data about blog posts to place the information in an ejs file.
I get the error:
Cannot read property 'send' of undefined
I've tried to call db.getPosts() with res and req, but it's not possible to set a header again, returns an error.
The problematic chunk of code in my query.js file:
const getPosts = (_req, res) => {
pool.query('SELECT * FROM blog_posts', (error, results) => {
console.log(error);
// console.log(results.rows);
if (error) {
throw error
}
return res.send(results.rows );
})
}
send(results.rows) or render('blog', {posts: results.rows}) called on res give the exact same error.
Function in server.js that is supposed to use this data is as follows:
app.get("/blog", function (req, res) {
const posts = db.getPosts();
res.render("blog", { posts: posts });
});
What do I do wrong? I lack some knowledge, that is for sure, so please, if you can help, explain this briefly to me if possible.
Also, is send() function a correct function to get the data to operate on in server.js? Many tutorials suggest json() but then I don't really get the proper data format, it is just displayed in the browser.
Thank you very much.
Make getPosts receive a callback:
const getPosts = (callback) => {
pool.query('SELECT * FROM blog_posts', (error, results) => {
console.log(error);
// console.log(results.rows);
if (error) {
throw error
}
callback(results.rows);
})
}
Usage would be something like:
app.get("/blog", function (req, res) {
db.getPosts(function(rows) {
res.render("blog", {posts: rows})
});
});
in your getPosts method do not use send. just return results.rows. upate your code like below.
const getPosts = () => {
pool.query('SELECT * FROM blog_posts', (error, results) => {
console.log(error);
// console.log(results.rows);
if (error) {
throw error
}
return results.rows;
})
}
also you need to use async await while calling getposts as it is a async function. update the code like below.
app.get("/blog", async function (req, res) {
const posts = await db.getPosts();
res.render("blog", { posts: posts });
});

Assign value to variable outside mongo query in nodejs

Right now i have this code
router.get('/export', function(req, res, next) {
var postData, eventData, messageData, userData
Posts.list().then(data=> {
var jsonOutput=JSON.stringify(data)
postData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
Events.list().then(data=> {
var jsonOutput=JSON.stringify(data)
eventData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
Messages.list().then(data=> {
var jsonOutput=JSON.stringify(data)
messageData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
Users.list().then(data=> {
var jsonOutput=JSON.stringify(data)
userData=jsonOutput //this doesnt work
})
.catch(erro => res.status(500).send('error'))
//Then when all data from colections is retrieve i want to use the 4 variables that i created in the beggining
});
So basicly im trying to retrieve the data from my mongo database and then assign the results to that 4 variables that i create, but im not getting success.
For what i´ve been seeing i have to use async but im having some trouble doing it.
I don't like too much mrlanlee solution. This is a typical situation where using async / await can really make sense. Anyway, the Hugo's solution (the second one, with async await), even if it just works, will make the four queries in sequence, one after another to. If you want a clean, working and parallel solution, check this:
router.get('/export', async function(req, res, next) {
let data
try {
data = await Promise.all([
Posts.list(),
Events.list(),
Messages.list(),
Users.list()
]);
// at this point, data is an array. data[0] = Posts.list result, data[1] = Events.list result etc..
res.status(200).json(data)
} catch (e) {
res.status(500).send('error');
}
});
The other answer from Sashi is on the right track but you will probably run into errors. Since your catch statement on each promise returns 500, if multiple errors are caught during the query, Express will not send an error or 500 each time, instead it will throw an error trying to.
See below.
router.get('/export', function(req, res, next) {
var postData, eventData, messageData, userData
try {
postData = Posts.list().then(data=> {
return JSON.stringify(data);
});
eventData = Events.list().then(data=> {
return JSON.stringify(data)
});
messageData = Messages.list().then(data=> {
return JSON.stringify(data);
})
userData = Users.list().then(data=> {
return JSON.stringify(data)
});
} catch (err) {
// this should catch your errors on all 4 promises above
return res.status(500).send('error')
}
// this part is optional, i wasn't sure if you were planning
// on returning all the data back in an object
const response = {
postData,
eventData,
messageData,
userData,
};
return res.status(200).send({ response })
});
For explanation of why you weren't able to mutate the variables, see Sashi's answer as he explains it.
The variables defined outside the async code is out of scope of the async functions. Hence you cannot store the returned value from the async functions in those variables.
This should work.
router.get('/export', function(req, res, next) {
var postData, eventData, messageData, userData
postData = Posts.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
eventData = Events.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
messageData = Messages.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
userData = Users.list().then(data=> {
var jsonOutput=JSON.stringify(data);
return jsonOutput;
}).catch(erro => res.status(500).send('error'));
});
Using Async/Await is a much neater solution.
router.get('/export', async function(req, res, next) {
var postData, eventData, messageData, userData;
try{
postData = await Posts.list();
eventData = await Events.list();
messageData = await Messages.list()
userData = await Users.list();
catch (e){
res.status(500).send('error');
}
});

How to access requested body from another function with async/await?

sorry for the generic title. I'm pretty new to nodejs as well as the idea of async/await.
So I have an express app, which makes an HTTP get request as a callback function. The callback function gets the body object, and returns it to getBody function. But when I try to assign getBody to a variable, it returns undefined.
Yes I know. The getBody function returns body before body gets filled up with data, but I just don't know how to write a getter method for this body object. So my question is, how can I run the get request and access body object in the global scope at the same time, so all functions depending on body object can run without further problems.
async function getBody (req, res, next) {
let body = await makeRequest(req, res);
return body; // THIS RETURNS UNDEFINED
}
router.get('/', getBody);
function makeRequest (req, res){
let uri;
let options = {
uri: uri,
};
request(options, function (error, response, body) {
if (error){
console.log('error:', error);
} else {
console.log('Success! statusCode:', response && response.statusCode);
let jsonObject = JSON.parse(body);
return jsonObject;
}
});
}
I did my research, but I just could not find a useful resource. Thanks in advance.
await and async should be used with a promise, these kind of method cannot return data. return is used to return a value from a synchronous method.
So you may return a promise from your makeRequest method like this,
async function getBody(req, res, next) {
let body = await makeRequest(req, res);
return body; // NOW BODY IS NOT UNDEFINED, call next() or send response here
}
router.get('/', getBody);
function makeRequest(req, res) {
return new Promise((resolve, reject) => {
let uri;
let options = {
uri: uri,
};
request(options, function (error, response, body) {
if (error) {
console.log('error:', error);
return reject(error);
} else {
console.log('Success! statusCode:', response && response.statusCode);
let jsonObject = JSON.parse(body);
return resolve(jsonObject);
}
});
})
}
FYI,
let body = await makeRequest(req, next)
is equals to
makeRequest(req, next).then(body => { /* YOUR CODE HERE */ })
and if you didn't knew, you have to process the body and send the response, return body won't send the response to the client.
OK, #JanithKasun did a great job of answering your original question fully. This answer is intended to expand on that a little to get a the problem you're having conceptually that isn't explicitly asked in your question.
As I understand your code, you're trying to fetch some data from a third party resource based on information in the request coming to your app's handler. Ideally, you want to separate your code in a way to make it more reusable/maintainable. I note that the code in question doesn't actually use the request or response object at all, but I'm going to assume you would have some kind of parameter to the getBody function that helps you construct the URI it's requesting. So, to that end:
// ./lib/get-body.js
const BASE_URI = 'https://example.com/search?q='
async function getBody (query) {
let body = await makeRequest(query);
return body;
}
function makeRequest(query) {
return new Promise((resolve, reject) => {
let uri = `${BASE_URI}{query}`; // results in something like 'https://example.com/search?q=cats'
let options = {
uri: uri,
};
// Note: removed console statements here to centralize error handling
request(options, function (error, response, body) {
if (error) {
return reject(error);
} else {
let jsonObject = JSON.parse(body);
return resolve(jsonObject);
}
});
})
}
// export the top level function for reuse
module.exports = getBody;
Now, in your routing code:
// ./index.js or wherever
const express = require('express');
const getBody = require('./lib/get-body');
//...whatever other setup...
app.get('/', async (req, res, next) => {
const query = req.query.terms; // or whatever
try {
const body = await getBody(query);
return res.send(body);
} catch (e) {
return next(e); // if you don't do this, process hangs forever on error
}
});
// handle the errors. obviously you can do something smart like
// figure out the error code and send back something other than a 500 if appropriate.
app.use((err, req, res, next) => {
console.error(err);
res.status(500).send('I am Bender; please insert girder.');
});
Hope that helps!

Caching datastore query

I'm not familiar with node programming, so I'm sure the error here is around syntax.
I'm trying to query datastore and store the returned entity as a const config reference in my function.
For some reason, if I invoke this as part of the body of the function I always get undefined instead of the value. Moving the invocation to the function that is called works, but I need the values to configure the function so it can't be on the first invocation.
Any ideas?
const Datastore = require('#google-cloud/datastore');
const datastore = Datastore();
const query = datastore
.createQuery('ProxyConfig')
.filter('function', '=', 'proxy');
const proxyConfig = loadConfig();
function loadConfig() {
datastore.runQuery(query).then(results => {
console.log("Query executed");
console.log(results);
return results;
});
}
exports.proxy = (req, res) => {
cnf = loadConfig();
console.log(`config is ${cnf}`);
res.status(200).send(cnf);
};
You're not returning anything from the function loadConfig which is why you're seeing undefined. With promises like that you want to return the promise and then get the results. By using .then and returning an item within that function the value will be wrapped inside another promise. So you can write this in two different ways. I'll first use the code above as is:
function loadConfig() {
return datastore.runQuery(query).then(results => {
console.log("Query executed");
console.log(results);
return results;
});
}
exports.proxy = (req, res) => {
const configPromise = loadConfig().then(results => {
const cnf = results;
console.log(`config is ${cnf}`);
res.status(200).send(cnf);
})
.catch(err => {
res.status(500).send(err);
});
};
However, since you are simply forwarding the results of the promise to the calling function this code can be simplified to:
exports.proxy = (req, res) => {
datastore.runQuery(query).then(results => {
const cnf = results;
console.log(`config is ${cnf}`);
res.status(200).send(cnf);
})
.catch(err => {
res.status(500).send(err);
});
};

Resources