Where should i put async and await?
result is
work2
work1
app.post('/upload', (req, res) => {
const txtupload = multer().any()
let validate = true
txtupload(req,res, (err)=>{
console.log('work1')
validate = false
})
if(validate){
console.log('work2')
//code
}
});
The upload function from multer doesn't return a promise, so async/await isn't applicable. Instead, issue your response in the callback.
Your real question seems to be: How do I use multer to handle file uploads? For that we go to the multer documentation. Adapting that documentation to your code:
app.post('/upload', (req, res) => {
const txtupload = multer().any();
txtupload(req,res, (err) => {
if (err) {
// ...it failed, send a failure response via `res`...
} else {
// ...it worked, send a success response via `res`...
}
});
// Don't do anything here, the upload hasn't been processed yet
});
But refer to the examples in the documentation, there are other patterns for using multer.
Since it's an arrow function, you have to put it before the parameters:
app.post('/upload', async (req, res) => { ... }
Related
I have a very simple route
router.get('/', async (req, res, next) => {
try {
const allEmployees = await employees.find({});
res.json(allEmployees);
} catch (error) {
next(error);
}
});
It works ok. But after I refactored it with catch. it stopped working and threw UnhandledPromiseRejectionWarning:
router.get('/', async (req, res, next) => {
const allEmployees = await employees.find({}).catch(next)
res.json(allEmployees);
});
It seems like the next is not called correctly in the second version. But Both should be equivalent in JavaScript. not sure why the second one is broken in Express.
Both should be equivalent in JavaScript
No, they are not.
Promise.prototype.catch() returns a new promise that resolves with the return value of the callback. Since next() returns void, this code...
employees.find({}).catch(next)
returns a successful promise, resolving with undefined. next() will be called with the failure but there's nothing stopping the rest of your code from calling res.json(undefined).
If you want to use the Promise prototype methods, the equivalent would be
router.get('/', (req, res, next) => {
employees.find({}).then(res.json).catch(next);
});
If you wanted to keep using async / await and have the eventual promise fail, you'd need something like this
router.get("/", async (req, res, next) => {
const allEmployees = await employees
.find({})
.catch((err) => Promise.reject(next())); // rejected result
res.json(allEmployees);
});
One of my express routes is giving me the following error:
Error: Route.post() requires a callback function but got a [object Promise]
It's because of the last line in controllerFunction:
// IN CONTROLLER.JS
exports.controllerFunction = async (req, res) => {
//Get some values
let foos = await User.somefunction();
for(foo in foos){
//Call some async void functions
}
res.status(200).send('DONE')
}
...
// IN ROUTER.JS
router.post('/', validation, validate, checkLoggedIn, checkUserPermission(1), async(req, res, next) => {
try{
await controller.controllerFunction(req, res)
} catch (error) {
next(error);
}
});
I figured I need to return a callback function, but I'm not sure how. The rest of my controller functions look like that and they work because of the .then():
// IN CONTROLLER.JS
exports.getCameras = async (req, res) => {
await Model.getCameras(req.machine)
.then(cameras => res.send(cameras));
}
But in my controllerFunction I can't call .then() due to the fact that I need to do some logic beforehand in a for loop, so I need to generate the callback function in some other way.
How could I make controllerFunction a callback function?
try this.
return res.status(200).send('DONE');
exports.controllerFunction = async (req, res) => {
//Get some values
let foos = await User.somefunction();
for(foo in foos){
//Call some async void functions
}
return res.status(200).send('DONE');
}
As #slebetman stated in the comments, one of the functions called in the router was incorrect:
router.post('/', validation, validate, checkLoggedIn, checkUserPermission(1), async(req, res, next) => {}
Make sure validation, validate and checkLoggedIn are all functions and not a return value
checkUserPermission(1) was incorrect since that function wasn't expecting any parameters to be passed, changed to checkUserPermission and it worked.
I was trying to make a routes for each ID I using a forEach loop but It stay loading until timeout reaches, all expected values are in place, all good but the second route is not running, I was fighting it despretly until now. I made sure there is a problem.
server.js
const router = require('express').Router();
function isAuthorized(req, res, next) {
if (req.user) {
next();
}
else {
res.redirect('/login')
}
}
let myguild = [];
router.get(`*`, isAuthorized, (req, res) => {
res.status(200);
console.log("wow");
console.log(req.user.guilds.length)
req.user.guilds.forEach(guild => {
myguild.push(guild);
})
console.log("Finished");
myguild.forEach(guild => {
console.log('Started')
router.get(guild.id, (req, res) => { // here is the problem
console.log("uh")
res.send("HAMBURGER")
console.log(req, res, guild)
})
console.log("Outed")
})
});
module.exports = router;
output:
wow
23
Finished
Started
Outed
Started
Outed
Started
Outed
Star... 'there is more but this is enough'
It should behave and run within server/${guild.id} but got (failed) request
Any Ideas?
You might need to redesign the API to better fit what you're trying to accomplish. If you already know which guilds are available then you'd need to create those before the server is initialized.
Even if they come from a database or are dynamic, you can loop through the guild "options" and create endpoints then provide access to them only if the user is qualified.
const { guilds } = require('./config')
const guildHandler = (req, res) => {
// Assuming you're doing more here
res.send('Hamburger')
}
guilds.forEach(guild => router.get(`/guilds/${guildId}`, guildHandler)
Or if you are NOT doingg something different in the middleware for each guild then you could just have a single route for guild.
router.get('/guilds/:guildId, guildHandler)
Not really sure what you're trying to accomplish but checkout out the Express docs. They solve most use cases fairly easily.
https://expressjs.com/en/api.html#req
You never call res.end() from your outer res.get() handler, so the request never completes.
And, with respect, creating route handlers like that in a loop is a mistake. It will lead to real performance trouble when your app gets thousands of guilds.
You'll want to use just one route, with a named route parameter, something like this.
const createError = require('http-errors')
router.get(':guildid', isAuthorized, (req, res, next) => {
const guildid = req.params.guildid
if (req.user.guilds.includes(guild)) {
console.log("uh")
res.send("HAMBURGER").end()
console.log(req, res, guildid)
} else {
next(createError(404, guildId + ' not found'))
}
})
Thanks for everyone helped.
Inspired answer
Final Result:
server.js
router.get('/:guildid', isAuthorized, (req, res, next) => {
console.log('started')
const guildid = req.params.guildid
if (req.user.guilds.some(guild => guild.id === guildid)) {
console.log('uh')
res.send("HAMBURGER").end()
} else {
res.sendStatus(404);
}
})
I have a node web server with several api endpoints. One of those endpoints needs to make a request to another server in order to assemble its own response. I've seen a few different answers with links to libraries that allow purely synchronous web requests, but they're all associated with warnings that say not to use them in production.
Here is an example of what my server request handler looks like:
app.get('/my_api_endpoint', function (req, res) {
// Need to get data from other webservice using data
// from this request.
data = request_to_other_web_service(req.params.some_value);
res.status(200);
res.send("This is the " + data);
res.end();
});
Is there a standard pattern for handling requests like this?
The reason why you are getting errors/warnings when trying to synchronously call an HTTP request is that from the get-go node.js was built with the core philosophy of callbacks & the event loop. If you are trying to write asynchronous code in a synchronous way... May i point you in the direction of async/await. You could you use npm install node-fetch
const fetch = require('node-fetch');
app.get('/my_api_endpoint', async function (req, res) {
// Need to get data from other webservice using data
// from this request.
data = await fetch("http://...");
res.status(200);
res.send("This is the " + data);
res.end();
});
In general all javascript libraries/frameworks are friendly to non-synchronous operations. Those that are not will be ignored by the community. Express already allow you to perform asynchronous operations.
Have you ever wondered why express requires you to "return" responses by calling methods on res instead of simply returning?:
// frameworks in some other languages:
#Path("/my_api_endpoint")
Response myEndpoint() {
data = request_to_other_web_service(some_value);
return data; // Javascript frameworks don't use returns!!
}
It is because all javascript web frameworks are async friendly. The reason you use a callback (a function that you call back at some later time) to return from a route is because you can call functions inside other functions. In Express you call res.send() after you've performed async operations. How you do it depends on the API you're using:
API that accepts a callback:
app.get('/my_api_endpoint', function (req, res, next) {
request(req.params.some_value, function (error, data) {
if (!error) {
res.status(200);
res.send("This is the " + data);
}
else {
next(error);
}
}
});
API that returns a promise:
app.get('/my_api_endpoint', function (req, res, next) {
request(req.params.some_value)
.then(function (data) {
res.status(200);
res.send("This is the " + data);
})
.catch(function (error) {
next(error);
});
});
Using async/await with API that returns a promise:
app.get('/my_api_endpoint', async function (req, res, next) {
try {
let data = await request(req.params.some_value)
res.status(200);
res.send("This is the " + data);
}
catch (error) {
next(error);
}
});
Stream based API:
app.get('/my_api_endpoint', async function (req, res, next) {
let dataStream = request(req.params.some_value);
let buffer = "";
dataStream.on('data', function (data) {
buffer += data.toString('utf8');
});
dataStream.on('end', function () {
res.status(200);
res.send("This is the " + buffer);
});
dataStream.on('error', function () {
next("Stream error");
});
});
Express can handle any asynchronous program flow.
I'm making a search-page on my server. When the endpoint is reached and the user waits for the search function to return the results and render the page Express falls through to the 404 handler instead, and I get the following error when I suppose the render function is called:
Error: Can't set headers after they are sent.
What am I doing wrong?
router.get("/", async (req, res) => {
try {
const queryString = req.query.q;
const user = helper.checkAndGetUser(req, res);
let s = String(queryString), searchedTags = [""];
if(s.indexOf(",") > -1){
searchedTags = s.replace(" ", "").split(",");
}
const options = {
"query": {tags: {$all: searchedTags}, _forSale: true}
};
const results = await Search.search(options).then(result => result).catch(err => {
throw err;
});
//This res.render -call is called after the 404 splat-route.
return res.render("partial/search.pug", {user: user, search: {
query: queryString,
results: results
}});
//If I'd use res.send for debugging, it is instead called before the splat-route, like the following:
return res.send(results);
} catch(err) {
next(err);
}
});
module.exports = router;
I register the router:
const search = require("./search.js");
app.use("/search", search);
Followed by the 404 splat-route:
app.get("*", async (req, res, next) => {
const user = helper.checkAndGetUser(req, res);
res.status(404);
res.render("partial/404.pug", {user: user});
});
To clarify:
My question is how can I make the res.render function get called just as the res.send function?
UPDATE [2017-10-05]:
I continued with another part of the site, a similar endpoint, and discovered that sending the result provided by the promise worked as expected if using res.send but not res.render. Using res.render the 404-handler kicked in again. Can this be a bug in Express?
This happens if you attempt to write to res after it is sent, so you must be calling additional code after res.render() or you already responded before calling that.
change it to return res.render(...) so it exits the functions, otherwise it will continue through the function and hit other res.render()s etc.
Something is up with that error handler also. I will update my post in a few mins with tips (on phone). It should probably have (req, res, next) and call return next(err) and pass it to your error handling middleware.
Here is the pattern I like to use in async/await Express:
// these routes occur in the order I show them
app.get('/route', async (req, res, next) => {
try {
const data = 'asdf'
const payload = await something(data)
.then((result) => createPayload(result))
// remember, if you throw anywhere in try block, it will send to catch block
// const something = willFail().catch((error) => {
// throw 'Custom error message:' + error.message
// })
// return from the route so nothing else is fired
return res.render('route', { payload })
} catch (e) {
// fire down to error middleware
return next(e)
}
})
// SPLAT
app.get('*', async (req, res, next) => {
// if no matching routes, return 404
return res.status(404).render('error/404')
})
// ERRORS
app.use(async (err, req, res, next) => {
// if err !== null, this middleware fires
// it has a 4th input param "err"
res.status(500).render('error/500')
// and do whatever else after...
throw err
})
Note: next() callback called without param is treated as no error, and proceeds to the next middleware. If anything is passed in, it will fire the error middleware with the param as the value of err in the error handling middleware. You can use this technique in routes and other middlewares, as long as the error middleware comes last. Mind your use of return with res.send/render() to prevent double setting headers.
NEW:
Something looks a little bit off with that .then() having a callback in it. I don't see logically where err would come from since the value of the resolved promise goes into the .then() function as result. At this point, it is suspect and should be removed or refactored if possible. This part here:
try {
let results = [];
await Search.search(options).then(result => {
results = result;
}, err => {
throw err;
});
console.log("res.render");
return res.render("partial/search.pug", {user: user, search: {
query: string,
results: results
}});
} catch(err) {
next(err);
}
First, here is about what I would expect to see with async/await syntax:
router.get("/", async (req, res, next) => {
try {
const queryString = req.query.q;
const user = helper.checkAndGetUser(req, res);
let s = String(queryString), searchedTags = [""];
if (s.indexOf(",") > -1) {
searchedTags = s.replace(" ", "").split(",");
}
const options = {
"query": { tags: { $all: searchedTags }, _forSale: true }
};
// If a promise is ever rejected inside a try block,
// it passes the error to the catch block.
// If you handle it properly there, you avoid unhandled promise rejections.
// Since, we have async in the route function, we can use await
// we assign the value of Search.search(options) to results.
// It will not proceed to the render statement
// until the entire promise chain is resolved.
// hence, then(data => { return data }) energizes `results`
const results = await Search.search(options)
.then(data => data)
// If any promise in this chain is rejected, this will fire
// and it will throw the error to the catch block
// and your catch block should pass it through to your
// error handling middleware
.catch(err => { throw 'Problem occurred in index route:' + err });
return res.render("partial/search.pug", {
user: user, search: {
query: string,
results: results
}
});
} catch (err) {
// look at the top how we added next as the 3rd, callback parameter
return next(err);
}
});
module.exports = router;
Error handler:
// notice how we add `err` as first parameter
app.use((err, req, res, next) => {
const user = helper.checkAndGetUser(req, res);
res.status(404);
res.render("partial/404.pug", {user: user});
});
From the Express docs:
Define error-handling middleware functions in the same way as other middleware functions, except error-handling functions have four arguments instead of three: (err, req, res, next). For example:
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
http://expressjs.com/en/guide/error-handling.html
That might be your true issue because the error handler should only fire if next() is called with any input, but yours appears to be firing every time like a normal middleware, so I suspect it's because there is no err parameter on that middleware function, so it is treated as a normal one.
The Default Error Handler
Express comes with a built-in error handler, which takes care of any errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack.
If you pass an error to next() and you do not handle it in an error handler, it will be handled by the built-in error handler; the error will be written to the client with the stack trace. The stack trace is not included in the production environment.
If you call next() with an error after you have started writing the response (for example, if you encounter an error while streaming the response to the client) the Express default error handler closes the connection and fails the request.
So when you add a custom error handler, you will want to delegate to the default error handling mechanisms in Express, when the headers have already been sent to the client:
// code example in docs
Note that the default error handler can get triggered if you call next() with an error in your code more than once, even if custom error handling middleware is in place.
I also recommend using that splat route app.get('*', async (req, res, next) => {}) right above the error handler middlware (aka as the last loaded route in your list). This will catch all unmatched routes, such as /sih8df7h6so8d7f and forward the client to your 404. I think the error handler middlware is more suited for error 500 and clean formatted type errors because it gives you a function that can parse the value of next(err) anytime it is called from a route.
I usually do this for authentication failures with JSON web token (as the first line of code inside every auth required route):
if (!req.person) return res.status(403).render('error/403')
I realize some of this may fry your wig wholesale, so try all this stuff out and see each piece working before you determine if you would like to utilize it or not.
After a few days of going through the code again and again I stumbled upon a problem in the checkAndGetUser-function, that when run without the user being signed in, and since it's faster than the async call to the DB, triggered the splat endpoint and thus showed the 404 page.
I believe the reason for not triggering the splat endpoint when the res.render call was replaced by res.send was that the res.send function is that much faster than the render-call, since it doesn't have to parse any HTML.
Thank you #agm1984 for providing very useful information about the Express framework, if anyone else are having the same or a similar problem, make sure to read through his post.