When I have a simple function processing the request I can use res.end() and return to end it at any point (some error happened/incorrect data, etc.)
get('/', function (req, res) {
if (!req.param('id')) {
res.send('Must provide ID!');
res.end(); // <-- response is ready, send it to client
return; // <-- request processing stops here, get() finishes
}
// do other stuff
res.send('ok'); // <-- this can never overlap with the previous res.send()
});
However, if there are functions embedded in other functions, return will only quit the last one
get('/', function (req, res) {
validate(req);
// do other stuff
res.send('ok'); // <-- this can cause errors? res was ended already
});
function validate(req, res) {
if (!req.param('id')) {
res.send('Must provide ID!');
res.end(); // <-- send response to client
return; // <-- this one exists only from validate()
}
}
I believe to send the response to client res.end() should be called, but how can I stop further code from processing - i.e. return from all functions?
It is impossible to return from a called function, just use a callback as below:
function validate(req, res, callback) {
if (!req.param('id')) {
res.send('Must provide ID!');
res.end();
} else {
callback();
}
}
get('/', function (req, res) {
validate(req, function () {
res.send('ok');
});
});
You could return true or false in validate function depending on if you had already sent response.
But, it's not node style. Using callbacks is preferred in node.
I know this is an old question but may be helpful for others. You could use res.headersSent like this
get('/', function (req, res) {
validate(req);
// will only be sent if validate hasn't already sent the error message
if(!res.headersSent) {
res.send('ok');
}
});
function validate(req, res) {
if (!req.param('id')) {
res.send('Must provide ID!');
res.end();
}
}
Related
How can I achieve this. In an easy way in ExpressJS?
App.get("/", async (req, res, next) => {
await something.catch(err => {
res.status(500).json("something wrong")
})
if (res.headersSent) return;
res.json("2st response");
});
Can I just call res.json() two time then express automatically understand to end response and not to send second response ? without of using middleware to check that response already send or not!
In your logic here with await, it's easier to just use try/catch instead of .catch() and that makes the flow a lot easier:
App.get("/", async (req, res, next) => {
try {
await something;
} catch(err) {
res.status(500).json("something wrong");
return;
});
res.json("2st response");
});
In general, you don't mix await with .catch() and partly for this reason because using await and try/catch makes code flow like this simpler since you directly return from the outer function in the try/catch(err) statement, but you cannot do that in the .catch() statement.
You can use return before res.send. This way it will send a response and exit the function.
app.get("/", (req, res, next) => {
return res.send("1st responce");
// Unreachable code
if(res.headersSent) return;
res.json("2st responce")
})
I'm working on a NodeJS/Express program and I'm trying to get a POST request to return data. When I return a direct string, I get the correct response.
app.post("/DoStuff", function(req, res, Data) {
DoStuf.DoStuffFunction(req.body.UserID, function(label) {
Data = label
})
res.send({message: "Stuff"});
})
When I change it to return a variable (which is still a string) it only returns "{}".
app.post("/DoStuff", function(req, res, Data) {
DoStuf.DoStuffFunction(req.body.UserID, function(label) {
Data = label
})
res.send({message: Data});
})
Even when I make Data = "foo" the response is "{}"
You need to send from inside of callback function. In your code res.send is independent of DoStuffFunction's callback
app.post("/DoStuff", function(req, res, Data) {
DoStuf.DoStuffFunction(req.body.UserID, function(label) {
Data = label;
res.send({message: Data});
})
})
Looks like your DoStuffFunction is async. So just move res.send(..) in callback.Something like
app.post("/DoStuff", function(req, res, Data) {
DoStuf.DoStuffFunction(req.body.UserID, function(label) {
res.send({message: label});
})
})
When I change it to return a variable (which is still a string) it only returns "{}".
This is because DoStuf.DoStuffFunction(){} is asynchronous.
The reason why it works when you use stuff as value is because the operation is synchronous. And you have value with you before sending the response.
If you want to send response only after the DoStuffFunction() completes, place the response.send() within the callback.
'use strict';
app.post("/DoStuff", function(req, res, Data) {
DoStuf.DoStuffFunction(req.body.UserID, function(label) {
res.send({message: label}); //you can send label's value directly
});
});
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.
After download the Zip file I need to call another function to ProcessZip file. But am unable to trigger the function ProcessZipFile() after the .send():
app.get('/', function (req, res) {
DownloadZipFile();
});
function DownloadZipFile() {
var file = fs.createWriteStream('./tmp/student.tar.gz');
s3.getObject(params
.on('httpData', function (chunk) {
file.write(chunk);
})
.on('httpDone', function () {
file.end();
})
.send();
}
function ProcessZipFile() {
//.....
}
As far as I know you can't call functions after you send a response to the browser. Because the route is finished. I have 2 ideas for you.
1: Make your DownloadZipFile() as a middleware and at success you go to ProcessZipFile() and then send the response()
2: Make a new route where you call ProcessZipFile() and call this route from the frontend via ajax for example
NodeJS is designed to be non-blocking, which means most I/O operations are asynchronous. You cannot simply call ProcessZipFile() after the .send() because this will trigger ProcessZipFile() before the download completes. Instead, you should call the function inside the success event handler which will be executed when the download completes.
function downloadZipFile(s3Params, downloadPath, callback) {
const file = fs.createWriteStream(downloadPath);
s3
.getObject(s3Params)
.on('httpData', function(chunk) {
file.write(chunk);
})
.on('success', function() {
// download succeeded -> execute the callback to proceed
callback(null);
})
.on('error', function(err) {
// download failed -> execute the callback to notify the failure
callback(err);
})
.on('complete', function() {
// close the file regardless of the download completion state
file.end();
})
.send();
}
function processZipFile(filePath, callback) {
// Process the file
// Remember to call `callback` after completion
}
app.get('/', function (req, res) {
const s3Params = { ... };
const filePath = './tmp/student.tar.gz';
downloadZipFile(s3Params, filePath, function(err) { // this callback function will be executed when the download completes
if (err) {
res.status(500).send('Failed to download the file.');
} else {
processZipFile(filePath, function(err) { // this callback function will be executed when the file is processed
if (err) {
res.status(500).send('Failed to process the file.');
} else {
res.send('File is downloaded and processed.');
}
});
}
});
});
I have a request handler for a particular route that does something like the following:
function doThing(req, res) {
res.json({ "thing" : "thing", "otherThing": externalModule.someFunction("yay"); });
}
It seems like the result is being send before the "someFunction" call completes, so the "otherThing" JSON is always non-existent. How can I wait for that function to return data before sending a response?
Use callbacks. Example:
externalModule.someFunction = function(str, cb) {
// your logic here ...
// ... then execute the callback when you're finally done,
// with error argument first if applicable
cb(null, str + str);
};
// ...
function doThing(req, res, next) {
externalModule.someFunction("yay", function(err, result) {
if (err) return next(err);
res.json({ "thing" : "thing", "otherThing": result });
});
}