Calling an API endpoint from within another route in Node / Express - node.js

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.

Related

How to pass and return data in the next middleware in NodeJs

I am trying to pass data from one middleware to another. The data is then returned to the client in the next middleware. I, however, am unable to catch it in the send.call.
How can I catch the data and send it?
Thank you all in advance.
const myPreMiddleware = async (req, res, next) => {
req.myData = myData;
next();
};
const myPostMiddleware = (req, res, next) => {
let send = res.send;
res.send = async function (body) {
send.call(this, req.myData); // The data i NOT accessible here
};
console.log("req.myData : " + JSON.stringify(req.myData)); // The data is accessible here
next();
};
app.use(myPreMiddleware);
app.use(myPostMiddleware);
Try pass the variable that you want to pass to the next() callback
here some example that will hopefully help
function notFound(req, res, next) {
res.status(404);
const error = new Error(`Not Found - ${req.originalUrl}`);
next(error);
}
function errorHandler(err, req, res, next) {
const statusCode = res.statusCode !== 200 ? res.statusCode : 500;
res.status(statusCode);
res.json({
message: err.message
});
}
app.use(notFound);
app.use(errorHandler);

How to consume data from firebase db in a middleware to authenticate users

What I'm trying to do is to consume data from a firebase databse. I'm trying to achieve that from a middleware, but I couldn't do that. I'm wondering if this is the best way to do this. This is my code:
exports.userExist = function (req, res, next) {
(async () => {
try {
var query = db
.collection("users")
.where("user", "==", req.query.user)
.where("password", "==", req.query.password);
query.get().then(function (querySnapshot) {
if (querySnapshot.size > 0) {
res.json(true);
next();
} else {
res.json(false);
next();
}
});
} catch (error) {
return res.status(500).send(error);
}
})();
};
My doubt is how can I consume this method from my middleware, I'm trying to do something like that:
function verifyUser(req, res, next) {
let user= userController.findUser; //Hi have doubt about of how consume the middelware..
if(user!=null){
//The rest of the code.
}
next();
}
Is it the correct approach? or maybe I'm trying to achieve this wrong?
Couple of problems with your code.
There is no need for an async IIFE inside a middleware. The middleware function itself can be an async function.
If you call res.json(), that ENDS the request, and sends the response. You likely don't want that behavior here.
exports.userExist = async function (req, res, next) {
try {
var query = db
.collection("users")
.where("user", "==", req.query.user)
.where("password", "==", req.query.password);
const querySnapshot = await query.get()
if (querySnapshot.size > 0) {
// assume the query only returns 1 user?
req.userObj = querySnapshot.docs[0].data()
}
next();
} catch (error) {
return res.status(500).send(error);
}
};
Then, in downstream handlers:
function verifyUser(req, res, next) {
if (req.userObj) {
// previous middleware found a user
} else {
// previous middleware did not find a user
}
next();
}
Example usage:
app.use(userExist)
app.use(verifyUser)

How to display an array like a hypertext in 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.
}
})

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

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