How to display an array like a hypertext in node.js - node.js

I want to display rooms array like hypertext but after the first room is created it gives an error.
let rooms = [];
app.get("/rooms", function(req, res) {
for(let i=0;i<rooms.length;i++) {
res.send(''+rooms[i]+' <br>');
}
res.send(200)
});
app.get("/room/:roomName", function(req, res) {
let roomName = req.params.roomName;
rooms.push(roomName);
res.send(roomName);
});

Actually the problem is with res.send since it shows that server have responded to client. So you can try following code
app.get("/rooms", function(req, res) {
let responseString = "";
for(let i=0;i<rooms.length;i++) {
responseString += (''+rooms[i]+' <br>');
}
res.status(200).send(responseString)
});

I think the code below fixes your problem,
// Rooms must be const, didn't modified, push is enumerating not overriding.
const rooms = []
// Prefer arrow functions for better looking code
// If you don't use req, just _
app.get('/rooms', (_, res) => {
// Map over rooms,
const response = rooms.map(room => `${room}`)
res.send(response)
})
app.get('/room/:name', (req, res) => {
const { name } = req.params
rooms.push(name)
res.send(name)
})

app.get('/rooms', (req, res) => {
try {
return res.status(200).json(rooms.map(room =>(`${room}`)));
} catch(error) {
console.error(error);
return res.status(500).json(error); // or another status you decide.
}
})

Related

Altering express session after res.render is executed

I am looking to store information generated by a post-route in the session, pass it on to a get-route (where I redirect after the post is done), run res.render() with that information from the session and then clear it so the session-variable is empty for the next route (Code block explains problem as well if this was too vague). Please keep in mind that the code below is a simplified version, my program contains a working session in which I can store and access variables.
const clearSessionMessage = async (req, res, next) => {
await res.on('finish', () => {
let sess = req.session;
sess.message = '';
});
next();
};
app.use(clearSessionMessage);
app.get('/route', (req, res) => {
let sess = req.session;
res.render('template', { message: sess.message });
});
app.post('/post/route' (req, res) => {
let sess = req.session;
sess.message = 'The message I want to pass';
res.redirect('/route');
});
Possible solutions I have tried:
Change the 'finish' parameter in res.on() to: finish, end or close
Force the session to save with req.session.save(callback)
After some research I have been able to solve my problem.
It is possible to edit the session after calling res.render(); but if you wish to do that you have to make sure you force the session to save with req.session.save(callback). This is because express will automatically save the session after a res.render();, edits after the render are not automatically saved!
app.get('/route', (req, res) => {
res.render('template', { message: req.session.message });
req.session.message = undefined;
req.session.save(err => {
if (err) {
throw err;
};
});
});
app.post('/post/route' (req, res) => {
req.session.message = 'The message I want to pass';
res.redirect('/route');
});
You don't really need to save or modify the session after a res.render call but save a local variable before destroying the session, which is then passed to the rendered page:
app.get('/route', (req, res) => {
let msg = req.session.message;
req.session.message = undefined;
res.render('template', { message: msg });
});
app.post('/post/route' (req, res) => {
req.session.message = 'The message I want to pass';
res.redirect('/route');
});

Using results from two different asynchronous functions

I'm trying to learn Asynchronous programming with NodeJS and I'm having trouble understanding how to create usable functions.
I'm trying to compare the results of a HTTP get request and a file read all inside an "express" callback. What is the best way to split out two different async operations into their own functions so that they can be used again together in a different callback?
I Have it working when I write everything inside the express callback
app.get('/', (req, res) => {
axios.get('http://127.0.0.1:8080')
.then(function(response) {
var http_data = response.data
// Do more stuff with data
fs.readFile('fwversion_current', 'utf8', function(err, contents) {
var file_data = contents.trim()
// Do more stuff with data
if (http_data == file_data) {
res.send("Match")
}
else {
res.send("No Match")
}
});
});
But I'm hoping for something more like this so I can use these same operations in other places. I'm not sure the right node way to get there.
function getHttpData() {
axios.get('http://127.0.0.1:8080')
.then(function(response) {
var http_data = response.data
// Do more stuff with data
return http_data
});
}
function getFileData() {
fs.readFile('fwversion_current', 'utf8', function(err, contents) {
var file_data = contents.trim()
// Do more stuff with data
return file_data
});
}
app.get('/', (req, res) => {
let http_data = await getHttpData()
let file_data = await getFileData()
if (http_data == file_data) {
res.send("Match")
}
else {
res.send("No Match")
}
});
You will need to wrap those functions inside a function that returns a Promise, this will let you the ability to await for them to complete before continuing.
function getHttpData(url) {
// axios.get already returns a Promise so no need to wrap it
return axios.get(url)
.then(function(response) {
let http_data = response.data;
// Do more stuff with data
return http_data;
});
}
function getFileData(path) {
return new Promise(function(resolve, reject) {
fs.readFile(path, function(err, contents) {
if (err) {
reject(err);
return;
}
let file_data = contents.trim();
// Do more stuff with data
resolve(file_data);
});
});
}
Now when both functions returns a Promise we can await for them to complete.
Make the handler an async function because it's needed to use the await keyword, I'm using Promise.all to fire both requests simultaneously and not wait for one to complete before we fire the other.
Wrap it in a try catch to handle errors and send status 500
app.get('/', async (req, res) => {
try {
const [http_data, file_data] = await Promise.all([
getHttpData(url),
getFileData(path),
]);
http_data == file_data
? res.send('Match')
: res.send('No Match');
} catch (err) {
console.error(err);
res.status(500).send('Something went wrong');
}
});

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');
}
});

Calling an API endpoint from within another route in Node / Express

I have myRoute.js with a route (GET) defined and I want to call an api endpoint from another route (api.js), and I'm not sure what the right way to do this is. The api.js route is working properly (image and code below).
api.js
router.get('/getGroups/:uid', function(req, res, next) {
let uid = req.params.uid;
db.getAllGroups(uid).then((data) => {
let response =[];
for (i in data) {
response.push(data[i].groupname);
}
res.status(200).send(response);
})
.catch(function (err) {
return err;
});
});
works as expected:
myRoute.js
I would like when a user goes to localhost:3000/USER_ID that the route definition gets information from the api. Psuedo code below (someFunction).
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
let fromApi = someFunction(`localhost:3000/getAllGroups/${uid}`); // <--!!!
console.log(fromApi) ; //expecting array
res.render('./personal/index.jade', {fromApi JSON stringified});
});
Not sure if i understand you correct but anyway i will try to help. So you have an api like
router.get('/getGroups/:uid', function(req, res, next) {
let uid = req.params.uid;
db.getAllGroups(uid).then((data) => {
let response =[];
for (i in data) {
response.push(data[i].groupname);
}
res.status(200).send(response);
})
.catch(function (err) {
return err;
});
});
If you would like to reuse it you can extract a function from the code above like so:
async function getAllGroupsByUserId(uid){
const result = [];
try{
const data = await db.getAllGroups(uid);
for (i in data) {
result.push(data[i].groupname);
};
return result;
}
catch(e) {
return e;
}
}
And then reuse it in your api & anywhere you want:
router.get('/getGroups/:uid', async function(req, res, next) {
const uid = req.params.uid;
const groups = await getAllGroupsByUserId(uid);
res.status(200).send(groups);
})
Same you can do in your another route:
router.get('/:uid', async function(req, res, next) {
const uid = req.params.uid;
const fromApi = await getAllGroupsByUserId(uid); // <--!!!
console.log(fromApi) ; //expecting array
res.render('./personal/index.jade', {fromApi JSON stringified});
});
Seems like pretty clear :)
I would use fetch for this. You can replace someFunction with fetch, and then put the res.render code in a .then(). So, you would get this:
const fetch = require("node-fetch");
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
fetch('localhost:3000/getAllGroups/${uid}').then(res => res.json()).then(function(data) {
returned = data.json();
console.log(returned); //expecting array
res.render('./personal/index.jade', {JSON.stringify(returned)});
});
});
A more robust way with error handling would be to write something like this:
const fetch = require("node-fetch");
function handleErrors(response) {
if(!response.ok) {
throw new Error("Request failed " + response.statusText);
}
return response;
}
router.get('/:uid', function(req, res, next) {
let uid = req.params.uid;
fetch('localhost:3000/getAllGroups/${uid}')
.then(handleErrors)
.then(res => res.json())
.then(function(data) {
console.log(data) ; //expecting array
res.render('./personal/index.jade', {JSON.stringify(data)});
})
.catch(function(err) {
// handle the error here
})
});
The ideal way would be to abstract your code into a method so you aren't calling yourself, as The Reason said. However, if you really want to call yourself, this will work.

Put and Delete Comment API endpoint - Express MongoDB/Mongoose

I have created an API endpoint that a client can post to and an API endpoint that retrieves the comments.
I am trying to create another API endpoint which allows the client to update an existing comment by specifying the id of the comment they want to change, and a final endpoint to delete. Here is what I have created so far:
var express = require('express');
var router = express.Router();
var Comment = require('../models/comments');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
/**
* Adds comments to our database */
router.post('/addComment', function(req, res, next) {
// Extract the request body which contains the comments
comment = new Comment(req.body);
comment.save(function (err, savedComment) {
if (err) throw err;
res.json({
"id": savedComment._id
});
});
});
/**
* Returns all comments from our database
*/
router.get('/getComments', function(req, res, next) {
Comment.find({}, function (err, comments) { if (err)
res.send(err); res.json(comments);
}) });
module.exports = router;
Here are examples of PUT and DELETE functions using ES6 syntax. The update function expects a title and content in the posted body. Yours will look different.
module.exports.commentUpdateOne = (req, res) => {
const { id } = req.params;
Comment
.findById(id)
.exec((err, comment) => {
let response = {};
if (err) {
response = responseDueToError(err);
res.status(response.status).json(response.message);
} else if (!comment) {
response = responseDueToNotFound();
res.status(response.status).json(response.message);
} else {
comment.title = req.body.title;
comment.content = req.body.content;
comment
.save((saveErr) => {
if (saveErr) {
response = responseDueToError(saveErr);
} else {
console.log(`Updated commentpost with id ${id}`);
response.status = HttpStatus.NO_CONTENT;
}
res.status(response.status).json(response.message);
});
}
});
};
module.exports.commentDeleteOne = (req, res) => {
const { id } = req.params;
Comment
.findByIdAndRemove(id)
.exec((err, comment) => {
let response = {};
if (err) {
response = responseDueToError(err);
} else if (!comment) {
response = responseDueToNotFound();
} else {
console.log('Deleted comment with id', id);
response.status = HttpStatus.NO_CONTENT;
}
res.status(response.status).json(response.message);
});
};
Usually when you update with PUT in a REST API, you have to provide the entire document's content, even the fields you are not changing. If you leave out a field it will be removed from the document. In this particular example the update function expects both title and content, so you have to provide both. You can write the update logic however you want though.
The router has functions for put and delete. So it will look something like this:
router
.get('comment/:id', getFunction)
.put('/comment/:id', putFunction)
.delete('/comment/:id', deleteFunction);

Resources