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).
Related
I am coding an e-commerce site with Node.js.
I noticed some code repeating while creating the routes but I couldn't find how to get rid of it.
men route is given below:
router.get(`/parent-category-selection`,(req, res, next) => {
categoryRequest.getAllParentCategories('mens', (error, data) => {
if(!error){
res.render('category/parentCategorySelection', {parentCategories:data});
}else {
res.render('error', {message:'An error occured.'})
}
})
})
women route is given below:
router.get(`/parent-category-selection`,(req, res, next) => {
categoryRequest.getAllParentCategories('womens', (error, data) => {
if(!error){
res.render('category/parentCategorySelection', {parentCategories:data});
}else {
res.render('error', {message:'An error occured.'})
}
})
})
routes in app.js:
app.use('/', indexRouter);
app.use('/men', menRouter)
app.use('/women',womenRouter)
app.use('/product',productRouter)
I want routes like /women/parent-category-selection and /men/parent-category-selection without code repetition.
How can I achieve that as you see above router functions are so similar I should find a way to bind gender information to the router like app.use('/:gender', genderRouter {gender:gender}). Any help ?
One pattern you could use is a higher order function, which is a function that returns a function. In this case, it is used to create an express middleware function. For example:
const parentCategorySelectionHandler = (gender) => (req, res) =>
categoryRequest.getAllParentCategories(gender, (error, data) => {
if (!error) {
res.render("category/parentCategorySelection", {
parentCategories: data,
});
} else {
res.render("error", { message: "An error occured." });
}
});
Which can be used like this:
router.get(`/parent-category-selection`, parentCategorySelectionHandler("men"));
If you wanted to get the gender from the URL, as you suggested, you could change it to the following.
middleware
const parentCategorySelectionHandler = (req, res) =>
categoryRequest.getAllParentCategories(req.params.gender, (error, data) => {
if (!error) {
res.render("category/parentCategorySelection", {
parentCategories: data,
});
} else {
res.render("error", { message: "An error occured." });
}
});
usage
router.get(`/parent-category-selection/:gender`, parentCategorySelectionHandler);
Then you'd need to change how you add the men/women routes in app.js since this one route would cover both of those genders.
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!
Ok so I am currently learning more node.js and decided to try out some basic middleware in a small api I created. I was wondering how I would wrap a successfull request. This is my approach.
Example Controller
exports.getTask = async function (req, res, next) {
try {
const task = await db.Task.findOne(
{
where: {
id: req.params.taskId,
userId: req.params.userId
}
});
if (task) {
req.locals.data = task;
res.status(httpStatus.OK);
next();
}
res.status(httpStatus.NOT_FOUND);
next();
} catch (err) {
next(err);
}
};
Middleware
exports.success = function(req, res, next) {
const success = res.statusCode < 400;
const successResponse = {
timestamp: new Date().toUTCString(),
success: success,
status: res.statusCode
};
if (success) {
successResponse.data = req.locals.data;
}
res.send(successResponse);
next();
};
I dont think its very good having to set req.locals.data for every requst and then calling next res.status(status) maybe I just approached the situation the wrong way?
How could you make this better?
In this case, probably using the express middleware concept (calling next()) will be an overkill.
I'd approach this by creating an abstraction for the success path. Consider something like this:
const resWithSuccess = (req, res, data) => {
res.json({
data: data,
timestamp: new Date().toUTCString(),
// success: res.statusCode < 400, // --> actually you don't need this,
// since it will always be true
// status: res.statusCode // --> or whatever else "meta" info you need
});
};
Then, as soon as you need to respond with success, go for it:
exports.getTask = async function (req, res, next) {
// .... bla bla
if (task) {
resWithSuccess(tank);
}
};
PS: ... and you can use the express middleware concept (calling next()) for the error path.
I am currently learning NodeJS and working on a mini app, trying to understand callback. However I keep getting the error:
callback(undefined,price);
^TypeError: callback is not a function
This is my code :
var getCoin = (coin, callback) => {
request({url:`https://https://rest.coinapi.io/${coin}&ssr=USD`,
json: true
}, (error, response, body) => {
if(error){
callback("error");
}
else if (response.statusCode == 200) {
let price = body.RAW[coin].USD.PRICE;
callback(undefined,price);
}
})
};
app.get('/', (req, res) => {
coin.getCoin('BTC', ()=> {
res.render('index.hbs', {
coin:'Bitcoin',
price: coin.getCoin('BTC')
});
});
});
Try this code:
app.get('/', (req, res) => {
coin.getCoin('BTC', (err, price)=> {
res.render('index.hbs', {
coin:'Bitcoin',
price: price
});
});
});
I am assuming coin.getCoin accepts two arguments, second argument is callback which itself needs to accept args to work properly. Now in app.get('/' you called coin.getCoin and passed an anonymous function that will be treated as callback for it, if coin.getCoin does its job correctly then price will have the value that will eventually be passed to index.hbs and res.render will do the rest job.
I am trying to make multiple HTTP requests and cumulate, display the results in NodeJS using the following code:
const async = require('async');
const request = require('request');
function httpGet(url, callback) {
const options = {
url : url,
json : true
};
request(options,
function(err, res, body) {
console.log("invoked")
callback(err, body);
}
).on('error', function(err) {
console.log(err)
});
}
const urls= [
"http://1.2.3.4:30500/status/health/summary",
"http://5.6.7.8:30505/status/health/summary"
];
async.map(urls, httpGet, function (err, res){
if (err)
console.log(err);
else
console.log(res);
});
The problem here is, if the first request(http://1.2.3.4:30500/status/health/summary) fails (like connection refused etc.), the second one does not go through. I know that I am making a silly mistake but cannot find it. Any help appreciated !
In async.map if one of the calls passes an error to its callback, the main callback (for the map function) is immediately called with the error(this is the problem in your case). In order not to terminate on the first error, don't call the callback with err param in your httpGet.
Use async each, it receives a list of arguments and a function, and calls the function with each element, make sure in your httpGet inside on error you call the callback, without the err, this will make rest of the calls to continue even if there was an error in some of the calls. This can work for map too but, I think the more suitable function for your case is async.each, instead of map, also you can limit the number of concurrent calls with eachLimit method.
Check https://caolan.github.io/async/docs.html#each
const async = require('async');
const request = require('request');
function httpGet(url, callback) {
const options = {
url : url,
json : true
};
request(options,
function(err, res, body) {
if (err){
console.log(err);
callback();
return;
}
console.log("invoked")
callback(null, body);
}
).on('error', function(err) {
console.log(err);
callback();
});
}
const urls= [
"http://1.2.3.4:30500/status/health/summary",
"http://5.6.7.8:30505/status/health/summary"
];
async.each(urls, httpGet, function (err, res) {
}, function (err, res) {
});
If you want async.map NOT to fail fast you could do it like this
const async = require('async');
const request = require('request');
function httpGet(url, callback) {
const options = {
url : url,
json : true
};
request(options,
function alwaysReportSuccess(err, res, body) {
callback(null, {
success: !err,
result: err ? err : body
});
}
).on('error', function(err) {
console.log(err)
});
}
const urls= [
"http://1.2.3.4:30500/status/health/summary",
"http://5.6.7.8:30505/status/health/summary"
];
async.map(urls, httpGet, function alwaysOk(_, res){
console.log(res); // will be an array with success flags and results
});