Add cookie to response object inside a get request in node JS? - node.js

I am trying to add a cookie to the response object (res) inside a different get request. However, if I try the following it will give me an error where I cannot set headers after they are sent. I assume by calling "request" I send the header already, how can I achieve adding a cookie to the response object using data from the separate get request? The synchronous nature won't let me save the data outside of the get request either. I am using request module from npm btw, thank you.
/* GET home page. */
router.get('/', function(req, res, next) {
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
var status = "Not currently logged in.";
if (req.cookies.token !== undefined) {
status = "Currently Logged in.";
}
if (req.cookies.email !== undefined) {
request('https://localhost:44338/api/customer/' + req.cookies.email
{json: true}, (err, response, body) => {
res.cookie('user', body[0].customerID, {maxAge: 9000000});
//console.log(body);
});
}
res.render('index', { title: 'Mighty Morphin Store', data: "", status: status});
});

You have to send your response in the callback function after setting cookies.
/* GET home page. */
router.get('/', function(req, res, next) {
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
var status = "Not currently logged in.";
if (req.cookies.token !== undefined) {
status = "Currently Logged in.";
}
if (req.cookies.email !== undefined) {
request('https://localhost:44338/api/customer/' + req.cookies.email
{json: true}, (err, response, body) => {
res.cookie('user', body[0].customerID, {maxAge: 9000000});
//console.log(body);
res.render('index', { title: 'Mighty Morphin Store', data: "", status: status});
});
}
});

Related

How to pass data between routes in express.js

hope someone can help me. Can't find a solution. Maybe I'm also just on the wrong way?
It's a simple express setup and I'm quite new.
I get a response from a request and want to pass a variable/the data from the response to the next route into the URL.
So one parameter in the next URL should be dynamical depending on the response of the first call.
here my whole code:
My problem is where you can see the const sendoutID
const express = require("express");
const app = express();
const request = require("request");
const bodyParser = require("body-parser");
const port = 3001;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Create Sendout
app.post("/createSendout", (req, res, next) => {
request.post(
{
url: "https://www.something.com/api/v1.2/surveys/904211/sendouts",
body: JSON.stringify(req.body),
headers: {
"Content-Type": "application/json",
"X-API-KEY": "xxxx-xxx-xxxx-xxx-xxxxxxx",
},
},
function (error, response, body) {
console.log(response.statusCode);
if (!error && response.statusCode == 200) {
// Successful call
var results = JSON.parse(body);
console.log(results.CreateSendoutResult.SendoutId); // View Results
// I want this data "results.CreateSendoutResult.SendoutId" passing to the next route
}
}
);
});
/* here the variable is just hard coded for now but
I want to pass it in the URL from my previous route
to the next route see below at + sendoutID +..*/
const sendoutId = 389125;
// Add Respondent
app.post("/addRespondent", (req, res, next) => {
request.post(
{
url:
"https://www.something.com/api/v1.2/surveys/904211/sendouts/" +
sendoutId +
"/respondents",
body: JSON.stringify(req.body),
headers: {
"Content-Type": "application/json",
"X-API-KEY": "xxxxxx-xxx-xxx-xxx-xxxxxxxx",
},
},
function (error, response, body) {
console.log(response);
//console.log(response.statusCode);
if (!error && response.statusCode == 200) {
// Successful call
var results = JSON.parse(body);
console.log(results); // View Results
}
}
);
});
app.listen(port, () => {
console.log(`app listening at http://localhost:${port}`);
});
To pass to the next route you can assign results.CreateSendoutResult.SendoutId to req.body
req.body.SendoutId = results.CreateSendoutResult.SendoutId;
Then you can use that SendoutId in next route.
You can pass that variable inside next()
next(results.CreateSendoutResult.SendoutId);
In the next route you can access it by calling:
function nextRoute(SenderId, req, res, next)
Edited:
const express = require("express");
const app = express();
const request = require("request");
const bodyParser = require("body-parser");
const port = 3001;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Create Sendout
app.post("/createSendout", (req, res, next) => {
request.post(
{
url: "https://www.something.com/api/v1.2/surveys/904211/sendouts",
body: JSON.stringify(req.body),
headers: {
"Content-Type": "application/json",
"X-API-KEY": "xxxx-xxx-xxxx-xxx-xxxxxxx",
},
},
function (error, response, body) {
console.log(response.statusCode);
if (!error && response.statusCode == 200) {
// Successful call
var results = JSON.parse(body);
console.log(results.CreateSendoutResult.SendoutId); // View Results
// I want this data "results.CreateSendoutResult.SendoutId" passing to the next route
req.SendoutId = results.CreateSendoutResult.SendoutId;
}
}
);
}, addRespondent);
/* here the variable is just hard coded for now but
I want to pass it in the URL from my previous route
to the next route see below at + sendoutID +..*/
const sendoutId = 389125;
// Add Respondent
app.post("/addRespondent", addRespondent);
function addRespondent(req, res, next) => {
request.post(
{
url:
"https://www.something.com/api/v1.2/surveys/904211/sendouts/" +
req.sendoutId +
"/respondents",
body: JSON.stringify(req.body),
headers: {
"Content-Type": "application/json",
"X-API-KEY": "xxxxxx-xxx-xxx-xxx-xxxxxxxx",
},
},
function (error, response, body) {
console.log(response);
//console.log(response.statusCode);
if (!error && response.statusCode == 200) {
// Successful call
var results = JSON.parse(body);
console.log(results); // View Results
}
}
);
}
app.listen(port, () => {
console.log(`app listening at http://localhost:${port}`);
});
You should be using a middleware function.
What is a middleware function?
-> it's just a function that runs in the middle that is (before a request hits the route and ends before a request completes.Express Documentation for writing middlewares.
Middleware functions are functions that have access to the request object (req), the response object (res), and the next function.NOTE middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging..
Advantage of using a middleware function -> "you can use this function for any other request too in the future", use it in other modules".
2)
const captureSendOutIDMiddleware = async (req, res, next) => {
try {
req.SendOutID=results.CreateSendoutResult.SendoutId; //changed "const req" to just req.
next();
} catch (error) {
res.status(401).send({
error: 'NO SEND OUT ID'
})
}
app.post("/CreateSendOut",captureSendOutIDMIddleware,async(req, res) =>{
//do your operation
});
app.post("/addRespondent",async(req,res)=>{
const capturedSendoutID=req.SendoutID;
console.log(capturedSendoutID);
//do you operations
});
Thanks for your help.
I found now another solution which works as well as I found another issue. Don't know if it was also a cause for why it didn't work, or why your solutions didn't work. But I used app.set() and app.get to pass the data.
The other issue was, that now, with app.set() and app.get() it sometimes worked, sometimes not. So I set a timeout on the call in the frontend, which executes the api requests. Just to have a bit time between.
here is my new code
// Create Sendout
app.post("/createSendout", (req, res, next) => {
request.post(
{
url: "https://www.something.com/api/v1.2/surveys/904211/sendouts",
body: JSON.stringify(req.body),
headers: {
"Content-Type": "application/json",
"X-API-KEY": "xxxxxx-xxx-xx-xxxx-xxxxx",
},
},
function (error, response, body) {
console.log(response.statusCode);
if (!error && response.statusCode == 200) {
// Successful call
var results = JSON.parse(body);
console.log(results.CreateSendoutResult.SendoutId); // View Results
app.set("surveyId", results.CreateSendoutResult.SendoutId); // new added line
}
}
);
});
// Add Respondent
app.post("/addRespondent", (req, res, next) => {
const surveyId = app.get("surveyId"); // new added line
request.post(
{
url:
"https://www.something.com/api/v1.2/surveys/904211/sendouts/" +
surveyId +
"/respondents",
body: JSON.stringify(req.body),
headers: {
"Content-Type": "application/json",
"X-API-KEY": "xxxxxx-xxx-xxxx-xxxx-xxxxxxxx",
},
},
function (error, response, body) {
console.log(response);
//console.log(response.statusCode);
if (!error && response.statusCode == 200) {
// Successful call
var results = JSON.parse(body);
console.log(results); // View Results
}
}
);
});

How to get an object data from a middleware?

I've been trying to create a checkout on my small business site and I'm trying to use a payment gateway API for it (documentation is subpar), the code below is my attempt to retrieve the paymentMedthod id, but i couldn't figure out on how to get that said id from a middleware funtion without doing this return next(body.data.id) which may cause issue because the middleware would stop and process won't proceed.
Thank you in advance, i know this is kind of dumb question but i really can't use any other API example like stripe in my country. this is the only option i have. Thanks.
e.g.
payment.js
module.exports = {
paymentMethod: function(req, res, next) {
...more lines of code here...
var options = {
...more here...
},
body: {
data: {
id: 1233445,
...more here...
}
},
json: true
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
//console.log(body);
return next(); //here lies the problem i'm avoiding to use return next(body.data.id) since it will stop the process and the route won't proceed to process what's inside of it.
});
},
paymentIntent: function(req, res, next) {
...more stuff here...
},
...more stuff here...
route.js
const Payment = require('../../config/payment');
//paymentMethod
router.post('/checkout', Payment.paymentIntent, Payment.paymentMethod, (req, res)=>{
//I'm confused on how to get the data coming from the middleware...
const id = Payment.paymentMethod(body.data.id);
...more stuff...
var options = {
...stuff..
...stuff..+id,
...stuff...
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body); //this should display the id coming from the middle ware
});
})
All middlewares share the same req and res objects, so you just have to assign your variable there.
payment.js
module.exports = {
paymentMethod: function (req, res, next) {
// ...
request(options, function (error, response, body) {
if (error) throw new Error(error);
req.paymentMethodId = body.data.id; // assign the ID to the req object
return next();
});
},
}
route.js
router.post('/checkout', Payment.paymentIntent, Payment.paymentMethod, (req, res) => {
const id = Payment.paymentMethod(req.paymentMethodId); // get the ID from the req object
// ...
})
Calling next(body.data.id) by convention actually triggers error handling, so the request will go to the error handling middleware (if you have one).

Is there a way to test error handling in ExpressJS with Mocha when using a custom error handler?

Test
it('should fail trying to GET bookmarks with false user id',async () => {
try {
const response = await request(app)
.get(baseApiUrlUnderTest + 'false_user_id/bookmarks')
.set('Authorization', bearerToken);
} catch (e) {
console.log(e); //it doesn't reach this point
expect(e.httpStatus).to.equal(HttpStatus.UNAUTHORIZED);
}
});
The relevant part of the method under test:
/* GET bookmark of user */
personalBookmarksRouter.get('/', keycloak.protect(), wrapAsync(async (request, response) => {
userIdTokenValidator.validateUserIdInToken(request);
...
}));
where wrapAsync makes sure the error is passed to the custom error handler:
let wrapAsync = function (fn) {
return function(req, res, next) {
// Make sure to `.catch()` any errors and pass them along to the `next()`
// middleware in the chain, in this case the error handler.
fn(req, res, next).catch(next);
};
}
The validateUserIdInToken method which causes the method under test to throw an exception:
const AppError = require('../models/error');
const HttpStatus = require('http-status-codes');
let validateUserIdInToken = function (request) {
const userId = request.kauth.grant.access_token.content.sub;
if ( userId !== request.params.userId ) {
throw new AppError(HttpStatus.UNAUTHORIZED, 'Unauthorized', ['the userId does not match the subject in the access token']);
}
}
module.exports.validateUserIdInToken = validateUserIdInToken;
and the custom error handler in the root middleware:
app.use(function(err, req, res, next) {
if (res.headersSent) {
return next(err)
}
if(err instanceof AppError) { //execution lands here as expected and the test stops...
res.status(err.httpStatus);
return res.send(err);
} else {
res.status(err.status || HttpStatus.INTERNAL_SERVER_ERROR);
res.send({
message: err.message,
error: {}
});
}
});
I think you may be approaching this incorrectly. Invalid auth should not raise errors in the app - it's not an error really, is a validation issue.
If the auth fails, simply send the relevant http error code - 401 back to the client.
res.send(HttpStatus.UNAUTHORIZED, 'a message if you want'); // 401
In your route handler:
personalBookmarksRouter.get('/', keycloak.protect(), wrapAsync(async (request, response) => {
const userId = request.kauth.grant.access_token.content.sub;
if ( userId !== request.params.userId ) {
return response.send(HttpStatus.UNAUTHORIZED);
}
...
}));
In your test, check the for status 401:
chai.request(server)
.get('/false_user_id/bookmarks')
.end((err, result) => {
if (err) {
return callback(err);
}
result.should.have.status(401);
});
Thanks to #laggingreflex's comment I missed debugging that the response actually returned with the expected status and error message
The adjusted test case now looks like this:
it('should fail trying to GET bookmarks with false user id',async () => {
const response = await request(app)
.get(baseApiUrlUnderTest + 'false_user_id/bookmarks')
.set('Authorization', bearerToken);
expect(response.status).to.equal(HttpStatus.UNAUTHORIZED);
});

Error: Can't set headers after they are sent in loop multiple request

i'm beginner at nodejs, i got a problem when request multiple url in a loop then i render it.
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:491:11)
at ServerResponse.setHeader (_http_outgoing.js:498:)
router.get('/', function(req, res, next) {
setInterval(function(){
request(url1,function (error,response,body) {
var data1 = JSON.parse(body);
request(url2+data1.access_token,function (error,response,body) {
var data_info = JSON.parse(body);
//error when it render
res.render('index', {data_info : data_info});
})
})
},5000);
});
That's not exactly a loop, I understand you mean that you call the same function repeteadly with setInterval().
Once you've sent your first response with res.render(), which finishes the response process for that request, subsequent attempts to use that res object fail.
If you want to send data to the client in 5 seconds interval you should probably either look into websockets or pass the setInterval() calls to the client so it polls your server each 5 seconds, in which case your server code could be changed to:
router.get('/', (req, res) => {
request(url1, (error, response, body) => {
const data1 = JSON.parse(body);
request(`${url2}${data1.access_token}`, (error, response, body) => {
const data_info = JSON.parse(body);
res.render('index', { data_info });
});
});
});
You can make use of Async Module
const async = require('async');
router.get('/', function (req, res, next) {
async.waterfall([
function(callback) {
request(url1, function (error,response,body) {
if(err) {
callback(err)
}else {
var data1 = JSON.parse(body);
callback(data1)
}
})
},
function(data1, callback) {
request(url2+data1.access_token, function(error,response,body) {
if(err) {
callback(err)
}else {
var data_info = JSON.parse(body);
callback(null, data_info)
}
})
}
], function(err, result) {
if(err) {
res.json({success: false, error: err, message: "Something went wrong.!"})
}else {
res.render('index', {
data_info : result
});
}
})
})

Instagram with NodeJS is throwing 404 error

var express = require('express'),
instagram = require('instagram-node').instagram(),
app = express();
instagram.use({access_token: 'MY_ACCESS_TOKEN'});
instagram.use({
client_id: 'my_client_id',
client_secret: 'my_client_secret'
});
app.get('/', function(req, res) {
instagram.use({ access_token: 'MY_ACCESS_TOKEN' });
instagram.media_popular(function(err, medias, remaining, limit) {
if (err) {
res.send(err.body);
}
else {
console.log(medias);
res.render('pages/index', { grams: medias });
}
});
});
app.listen(8888,function(){
console.log("listening port 8888")
});
So, this is my code. When I'm hitting the http://localhost:8888, I'm getting the following page
What am I doing wrong?
Why am I not logged in when I get the 404 page?
Not really sure what is the problem but as an workaround you can use requestjs to send GET request using this
https://api.instagram.com/v1/media/popular?access_token=ACCESS-TOKEN
url. You will get the same data. Might not be a clear solution but I know that work for sure.
check your access token while passing through the URL. I got this page when my URL was not correct.
You can use this code after getting access token for getting user data from instagram.
exports.getUserData = function(req, res) {
var outputJSON = "";
var requestUrl = 'https://api.instagram.com/v1/users/self/?access_token=YOUR ACCESS TOKEN HERE';
request.get({ url: requestUrl, json: true }, function(err, response, accessToken) {
if (response.statusCode !== 200) {
outputJSON = { 'status': 'failure', 'messageId': 403, 'message': err };
} else {
outputJSON = { 'status': 'success', 'messageId': 200, 'data': response.body }
//console.log('response',response.body);
}
res.jsonp(outputJSON);
});
}

Resources