When does the cycle's code execution stop in NodeJS Express? - node.js

After reading Node.JS Express documentation, I'm not clear about when the client cycle is finished after he requests something.
Does res.json(), res.send() and res.render() finish the cycle entirely? By entirely I mean: all the code execution in the server for that request. Or is it that the the client perceives a cycle finish by receiving a response from the server but the server continues to execute code?
For example, if I have this:
router.get('/home/about', function(req, res) {
// code block #1
// ...
if (condition) {
res.render('blah.ejs');
}
// code block #2
// ...
res.render('about.ejs');
});
If condition is true, I have a few questions:
Is code block #2 executed?
Is res.render('about.ejs') executed?
If the answer to above question (2) is true, what happends with that response? because I'm pretty sure that the user will receive the response of res.render('blah.ejs')
Also, what changes if I write return; below res.render('blah.ejs)`

The function doesn't exit when you call res.render or any of the other examples you showed, but the pipe will be closed shortly after you call it so in most cases your example will result in an error that you tried to write to a closed pipe.
The best way to do what you're showing is something like this:
if(condition){
return res.render('blah.ejs');
}
res.render('about.ejs');
This way when the code hits that 'return' statement, nothing else in the function scope will execute.
You could also (and many folks do) throw in a return statement before the second res.render call just to be explicit.

Related

Behavior of expressjs next middleware when the response has gone

I am using express (v:4.17.1) and here is my code snippet:
//upload.array is for multer npm file uplaoding....
//LIMIT is how many files you can upload via multer ....
app.post('/processFiles', upload.array('myFile', LIMIT), (req, res, next) => {
if (!validEnv) {
res.setHeader('X-Correlation-ID', corrId);
res.status(400).json({'error':'invalid env'});
return next(); //A
}
//checking how many file hvae been uplaoded
if (totalFiles > LIMIT) {
res.status(400).json({'error':'Too many files uploaded'});
return next(); //B
}
//Some xml parsing code and later it inserts into db
}
Few questions on the way I have added 'return next();' in the routes. Please note that I have not added any explicit middleware, just relying on what express is providing:
If I keep [A] or [B] as it is, then it gives a proper error message to the browser if the conditions get TRUE and returns the response.
But if I comment the line //A or //B, it gives the error response back to browser but also prints few error logs as mentioned below:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I don't know why the above line is appearing when I comment the //A or //B (but wont complain if I keep as it is without commenting them) as the response was already out to the browser and the express will not execute any piece of code after the response has gone.
Please correct my understanding ?
Also, Is this the correct way to handle errors as I have coded? If No, what could be the proper way, please advice? Is there any way by which we can see the complete middleware execution in sequence until the last one via any debug flags
There are a several ExpressJS things to know that apply here:
next() tells Express to continue routing to other requests. Only call next() if you want other routes to continue to be able to process this request and send a response. Or the corallary, don't call next() if you've already sent a response.
You can only send one response for a given request. Once, you've called res.send() or .res.json() or any other way of sending a response, you should be completely done with your request processing and no other code should try to send a response.
res.send() and next() are not Javascript flow control. If you want to stop further processing of the request in your function, you need to code that with traditional Javascript flow control such as return, if/else, etc... to prevent the rest of your code from continuing to run or process the request.
To that end, your code should look like this:
//upload.array is for multer npm file uplaoding....
//LIMIT is how many files you can upload via multer ....
app.post('/processFiles', upload.array('myFile', LIMIT), (req, res, next) => {
if (!validEnv) {
res.setHeader('X-Correlation-ID', corrId);
res.status(400).json({'error':'invalid env'});
return;
}
//checking how many file hvae been uplaoded
if (totalFiles > LIMIT) {
res.status(400).json({'error':'Too many files uploaded'});
return;
}
// more code here that sends some other response
}
But if I comment the line //A or //B, it gives the error response back to browser but also prints few error logs as mentioned below: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
This error occurs when you attempt to send more than one response for the same request (e.g. call res.send(...) more than once. Your code should not allow that. Once you send a response, your code should use normal Javascript flow control (such as return or if/else) to avoid executing any more code that might send a response. Typically after you send a response, you are done processing the request and would just return from the request handler.
This is happening because of the //A. See, what is going on here in your code :
If you comment line //B it reaches the next if (totalFiles > LIMIT) which is sending another response but above the return next() is called. That is why it is generating error that once they are sent in if(!validEnv) how can Headers be sent again!
You have two if conditions that mean both might get executed, in both the conditions you are setting the headers and ending the request. Once the request is ended the headers cannot be set. So if you comment [A] which has the return statement and if the second condition is also satisfied, it will set the headers again after sending the request. so it has nothing to do with the next() function or express since there is no next middleware in the stack.
Also the error handling is looks fine. however you can use the node --inspect flag while starting the script and set breakpoint in your middleware to debug entire node code.

Handling multiple post requests with locking

So I have to write some NodeJS code that does the following: whenever a post request is made, I attempt to execute some program; if the program is already executing (because of a previous request), I ignore the request. If not, I execute the program. I'm using NodeJS child_process.exec to accomplish this; however, there's no way for me to know when exec(program) terminates; I thought of using execSync, but this simply blocks any requests until the program is done executing, instead of ignoring them completely. Here is the code I have right now:
function fun () {
execFile('C:\\Windows\\System32\\notepad.exe', ['package.json'],);
}
execFile is an EventEmitter, so you can listen for events that occur while execFile operates, including the exit event, which tells you the process has completed.
ignoreNextRequest = true;
execFile('C:\\Windows\\System32\\notepad.exe', ['package.json']).once('exit', (code, signal) => {
// Your code to handle the end of the process here.
ignoreNextRequest = false;
});

Node.js/Express.js: does response.send() can have a callback?

My question is can i execute business logic once the res.send() had successfully ended?
I'm using Node.js and Express.js.
Something like: (The following code will not work)
res.send("Hello World",function(err,res){
log(err + " " + res); // Will execute once res.send succeed and the user received "Hello World"
});
Thanks!
.send() does not have a callback as it is not an asynchronous method. Also, it will never throw an error.
You can continue on writing code the method and you will be sure that the response has already been sent. Obviously, you won't be able to add anything to the response variable afterwards since it's already been sent to the user.

Why can't we do multiple response.send in Express.js?

3 years ago I could do multiple res.send in express.js.
even write a setTimeout to show up a live output.
response.send('<script class="jsbin" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>');
response.send('<html><body><input id="text_box" /><button>submit</button></body></html>');
var initJs = function() {
$('.button').click(function() {
$.post('/input', { input: $('#text_box').val() }, function() { alert('has send');});
});
}
response.send('<script>' + initJs + '</script>');
Now it will throw:
Error: Can't set headers after they are sent
I know nodejs and express have updated. Why can't do that now? Any other idea?
Found the solution but res.write is not in api reference http://expressjs.com/4x/api.html
Maybe you need: response.write
response.write("foo");
response.write("bar");
//...
response.end()
res.send implicitly calls res.write followed by res.end. If you call res.send multiple times, it will work the first time. However, since the first res.send call ends the response, you cannot add anything to the response.
response.send sends an entire HTTP response to the client, including headers and content, which is why you are unable to call it multiple times. In fact, it even ends the response, so there is no need to call response.end explicitly when using response.send.
It appears to me that you are attempting to use send like a buffer: writing to it with the intention to flush later. This is not how the method works, however; you need to build up your response in code and then make a single send call.
Unfortunately, I cannot speak to why or when this change was made, but I know that it has been like this at least since Express 3.
res.write immediately sends bytes to the client
I just wanted to make this point about res.write clearer.
It does not build up the reply and wait for res.end(). It just sends right away.
This means that the first time you call it, it will send the HTTP reply headers including the status in order to have a meaningful response. So if you want to set a status or custom header, you have to do it before that first call, much like with send().
Note that write() is not what you usually want to do in a simple web application. The browser getting the reply little by little increases the complexity of things, so you will only want to do it it if it is really needed.
Use res.locals to build the reply across middleware
This was my original use case, and res.locals fits well. I can just store data in an Array there, and then on the very last middleware join them up and do a final send to send everything at once, something like:
async (err, req, res, next) => {
res.locals.msg = ['Custom handler']
next(err)
},
async (err, req, res, next) => {
res.locals.msg.push('Custom handler 2')
res.status(500).send(res.locals.msg.join('\n'))
}

Meteor blocking clarification

According to the meteor docs, inserts block:
On the server, if you don't provide a callback, then insert blocks
until the database acknowledges the write, or throws an exception if
something went wrong. If you do provide a callback, insert still
returns the ID immediately.
So this would be wrong:
Meteor.methods({
post: function (options) {
return Stories.insert(options)
}
});
I need to do this:
Meteor.methods({
post: function (options) {
return Stories.insert(options, function(){})
}
});
Can somebody confirm that this is the case? The former will block the ENTIRE SERVER until the db returns?
Yeah, it will block, but not the entire server.
In Meteor, your server code runs in a single thread per request, not in the asynchronous callback style typical of Node. We find the linear execution model a better fit for the typical server code in a Meteor application.
So, if you are worried about that it will block the entire server as it will do in typical Node, don't be.

Resources