I have this route that queries an API to get information about the project. It uses super agent to post a get request, passes along some headers with the .set and project_results should contain the data.
Now here is the issue: I can load the page for this particular route/project 20 times... 19 of the times it will work perfectly but randomly it will throw an error:
TypeError: Cannot read property 'creator' of null
This error points out the line: with if(project.creator == req.signedCookies.user_3rb._id) {
So I know it is making it past the: if(project_results.status == '200') {
and since I am looking at the same project over and over and over (and I know all projects have a creator I checked the DB)
my question would be why sometimes does it not find this property of the project_results variable? Its very inconsistent.. I would think project_results is completely populated before going through the code... since its passing the status check we know there is at least some data in the project_results variable..
app.get('/user/projects/:project_id', function(req, res, next) {
var agent = superagent.agent();
var project = {};
project.id = req.params.project_id;
agent
.get(apihost + '/api/project/'+project.id)
.set('api_key', apikey)
.set('access_token', user.access_token)
.end(function(project_error, project_results) {
if (project_error) {
console.log(project_error);
}
if(project_results.status == '200') {
project = JSON.parse(project_results.text);
// Check if we are the owner of the project
if(project.creator == req.signedCookies.user_3rb._id) {
project.owner = true;
}
......
I'm one of the contributors to superagent.
I'd recommend isolating a test case instead of trying to debug directly in your app.
One simple way is to create a very short express app that always returns a hardcoded, perfect response to the route you're trying to reach - then see if project is still null 1 in 20 times:
var app = express();
app.use(function(req, res, next) {
res.send({
// your data hardcoded here
});
});
app.listen(3000);
If it's still null 1 in 20 times, then please submit it as an issue and I'll check it out. If not, then likely something else is going on - perhaps the data isn't being consistently fetched from the db, or there's a race condition between the fetch and the rendering.
Related
I am building an API to manage meetups with nodeJS. I have build an endpoint with the route "/meetups/:id/" to fetch a specific meetup record by its id. And then I want to fetch all the upcoming meetup records and I tried to use "/meetups/upcoming/" but when I query it, I get the not found error (404). It seems like the second route is not recognised.
Here is the code defining the two routes
the request from postman
Any help on how can I handle that?
Thanks.
Route is '/api/v1/meetups/upcoming/all'. Move res.status outside the map function.
EDIT: you'll have to change the route which has to be different from api/v1/meetups/:id. Reason is when route '/api/v1/meetups/upcoming' is requested express sees it as the same route as before and takes 'upcoming' as the parameter.
app.get("/api/v1/meetups/upcoming/all", function(req, res) {
var today = new Date();
var upcomings = db.meetups.map(function(meetup) {
if(meetup.happeningOn > today) {
return meetup;
}
});
res.status(200).send({
status: 200,
data: upcomings
});
});
You need to move the res.status piece outside of the const upcomings definition.
I've currently written middleware to verify an ID exists in an external services (Salesforce). I initially wrote it when it was a single use app, but now I'm trying to make it work with different routes, so I want it to be fairly generic.
I don't even know if middleware is the right way to go, or if I should just call the function before saving the specific form.
I've got a form where someone puts in some information about a project, and the salesforce ID. For background, the salesforce ID is actually an auto-increment number, and I need to convert that to the actual salesforce system ID before I use jsForce to create a new object linked to that ID.
My route looks like this:
router.post('/invoice/add', ensureLoggedIn, invoiceController.validateInvoice, catchErrors(sfdc.validateSFID), catchErrors(invoiceController.saveInvoice))
So, I've got a middleware that does this:
exports.validateSFID = async(req, res, next) => {
const salesforceProjectNumber = req.body.SF_Opportunity
const sfResult = await conn.search(`FIND ... long query`, (err, result) => {
if (err || result.searchRecords.length !== 1) {
req.flash('error', 'Unable to find a Salesforce Job with that ID number.')
console.error(`ERROR: ${req.user.displayName} errored when looking up job number ${salesforceProjectNumber}.`)
return result
}
})
if (sfResult.searchRecords.length > 0) {
req.body.salesforce_Opportunity_id = sfResult.searchRecords[0].Id //Create a generic variable to hold the salesforce opportunity so it works regardless of the custom object name
res.locals.Opportunity_Clean_Name = sfResult.searchRecords[0].Name
}
next()
}
The query rarely throws an error, but in this case, an error is basically returning !1 records.
When that happens, I want to flash a message on the screen saying the ID wasn't found, but keep the form filled in.
When an ID is found, I want to proceed to save it and NOT display the form fields anymore.
This middleware needs to work regardless of the form I'm using, I want to be able to pipe in the middleware from any form that might require a user to enter a salesforce job as a field.
Any thoughts on how best to handle it all?
You can use your middleware by using app.use() function
app.use((req, res, next) => {
// Every time a request has made, this middleware will fire
console.log('Howdy');
})
I am sorry that I can't come up with a better title.
I always have this problem (when coding in node.js also python) but I think my solution is kind dirty.
I am here to seek a better solution for this problem.
Here is the scenario:
Your server is doing a very very heavy task upon a special http request (like generating browser screenshot for an URL/generating game server banner with statistics). Whoever did a HTTP request to your server will get the same response. The response will be cached for a long time.
For example, in the browser screenshot generating HTTP request, your server is expected to spawn a phantomjs, capture the screenshot, save it and cache it for a long time, then respond with the PNG captured. The HTTP request after this should hit the cache.
The pseudo code to scenario:
server.get(":urlname.png", function(req, res, next) {
var cached = cache.get(req.params_urlname);
if (cached) {
res.send(cached);
return;
}
// This will take very long time
generateScreenshot(req.params_urlname, function(pngData) {
cache.set(req.params_urlname, pngData, LONG_TIME);
res.send(cached);
});
});
Here is the problem:
Imagine that you have a screenshot generating URL
(http://yourserver.com/generate-screenshot/google.png). The screenshot
is not generated nor cached yet.
Your posted the URL in a very popular forum, and there are 1000 HTTP requests to the that URL at the same time! It means that your server will have to spawn 1000 phantomjs and all of them together will generate screenshot of google.com at the same time, which is crazy!
In other words, the heavy function should be executed only once for generating cache.
My current code solution to the problem:
var pendingResponse = {};
server.get(":urlname.png", function(req, res, next) {
var cached = cache.get(req.params_urlname);
if (cached) {
res.send(cached);
return;
}
// The screenshot is currently generating for other request. Let's mark this response as pending.
if (req.params_urlname in pendingResponse) {
pendingResponse[req.params_urlname].push(res);
return;
}
// The screenshot needs to be generated now. Let's mark the future response as pending.
req.params_urlname[req.params_urlname] = [];
// This will take very long time
generateScreenshot(req.params_urlname, function(pngData) {
cache.set(req.params_urlname, pngData, LONG_TIME);
res.send(cached);
// Let's respond all the pending responses with the PNG data as well.
for (var i in pendingResponse[req.params_urlname]) {
var pRes = pendingResponse[req.params_urlname][i];
pRes.send(cached);
}
// No longer mark the future responses as pending.
delete pendingResponse[req.params_urlname];
});
});
This solution works. However, I consider this solution dirty, because it not reusable at all. Also, I think it may cause resource leak. Is there any better solution / library?
Here's a proof-of-concept server doing this result caching using a memoizee package (not only removes the necessity to cache computations in progress, but also allows to remove the "cache" altogether):
var express = require('express');
var memoize = require('memoizee');
function longComputation(urlName, cb) {
console.log('called for ' + urlName);
setTimeout(function () {
console.log('done for ' + urlName);
cb();
}, 5000);
}
var memoizedLongComputation = memoize(longComputation, {async: true, maxAge: 20000});
var app = express();
app.get('/hang/:urlname', function (req, res, next) {
memoizedLongComputation(req.params.urlname, function () {
res.send('hang over');
});
});
app.listen(3000);
Here we make the result be cached for 20 seconds.
When I start the server and then run in the shell
for i in `seq 1 10`; do curl http://localhost:3000/hang/url1; done
(or just open several browser tabs and quickly navigate them all to http://localhost:3000/hang/url1), I see one "called for url1" and in 5 s one "done for url1" message in the console, meaning only one "real" longComputation call was made. If I repeat it shortly after (less than in 20 s), there are no additional messages, and results are returned instantaneously, because they are cached. If I repeat the command later (in more than 20 s), there's again one call only.
Sometimes we need restrict executing repeating requests from a user if the first request has not been finished. For example: We do want register user at some service, and only after that put him into database with external id. I would like to have a service where I can set protected routes.
I have solved this problem by checking flag at request.start() -> and removed it after the request has been completed. Anyway I'm looking for your suggestion guys.
I think you need to build this into the route handlers yourself:
var inProgress = []
var handleRegisterRoute = function create(req, res, next) {
var id = req.user.id
var found = _.find(inProgress, function(item){
return item = id
})
if(found){
res.send('Dont think twice, its aright')
return
} else {
inProgress.push(id)
completeRegister()
inProgress= _.without(inProgress, id);
req.send()
return
}
}
Again this is psuedo code - to the gist of what I would write. You may need to store the "inprogress" in a better data store referenceable by your entire server farm - some sort of DB.
I've been building my first node.js app using express.js.
It has been fun :)
I must be having some kind of misconception going, so here goes.
I have some route defined as such:
app.all('/account/summary', apiPOST('AccountSummary', {FromDate: 'default', ToDate: 'default'}), function(req, res){
var data=req.apiJSON;
console.log(data);
res.render('accountsummary', {locals: data, layout: 'layouts/layout'});
});
apiPOST() is defined as such:
apiPOST = function (operation, postParams) {
return function (req, res, next){
console.log('RIGHT AT START');
console.log(postParams);
console.log('END');
var currentDate = new Date();
var day = ('0'+currentDate.getDate()).slice(-2);
var month = ('0'+(currentDate.getMonth() + 1)).slice(-2);
var year = ('0'+currentDate.getFullYear()).slice(-4);
console.log('BEFORE DATES');
console.log(postParams);
if (typeof(postParams.FromDate)!='undefined' && (postParams.FromDate=='default' || postParams.FromDate=='')){
postParams.FromDate=year+'-'+month+'-'+day;
}
if (typeof(postParams.ToDate)!='undefined' && (postParams.ToDate=='default' || postParams.ToDate=='')){
postParams.ToDate=year+'-'+month+'-'+day;
}
//automatically add all posted data to postParams
if (typeof(req.body)!='undefined'){
for (var key in req.body){
if (req.body.hasOwnProperty(key)){
postParams[key]=req.body[key];
}
}
}
// here is do some talking to an XML web service and convert it to JSON;
// we use our postParams variable to POST
next();
}
}
First off this works fine. When reaching the page on a GET request, it defaults both FromDate and ToDate to today. This page has a form that you may post to specify a new FromData and ToDate. the posted data automatically get added to the postParams and that also works fine.
The problem that I am experiencing is that the next time a user visits the page using GET, the previously POSTed data is still around and so it default to that and not to today.
I cannot figure out why that data is still available. By the looks of it, it is not being posted, but rather remembered in postParams. Is postParams now global?
Thanks!
What's happening is that you're calling apiPOST only once, during the app.all call to configure that route and that one call creates the one postParams parameter object that all future invocations of apiPOST's returned function will share.