I have a weird situation...
I have a Express.js, Node.js and Mongoose web app.
One of the routes has a mongoose callback that calls respond.send(...). However, because there is nothing else after the callback, I suspect it goes to next() route automatically.
Ex:
//getItem
app.get('/api/ItemOne', isUserValid, routes.getItem);
//getAnotherItem
app.get('/api/getAnotherItem', isUserValid, routes.getAnotherItem);
//Routes
exports.getItem = function (req, res) {
//console.log ('In getItem');
getItem .findOne({ some_id : some_id}, function(err, getItem ){
//console.log ('In getItem callback');
res.send({
itemName : getItem .itemName,
itemValue : getItem .itemValue;
});
})
});
exports.getAnotherItem = function (req, res) {
//console.log ('In getAnotherItem');
getAnotherItem.findOne({ some_id : some_id}, function(err, getAnotherItemRet){
//console.log ('In getAnotherItem Callback');
res.send({
itemName : getAnotherItemRet.itemName,
itemValue : getAnotherItemRet.itemValue;
});
})
});
I get the following sequence of messages on the console...
In getItem
In getAnotherItem
In getItem callback
In getAnotherItem callback
I am assuming because the route does not finish, it calls next() automatically.
Q: How do I prevent the second route from being called automatically.
Try reversing req and res:
// wrong
exports.getItem = function (res, req) {
// right
exports.getItem = function (req, res) {
(and same for getAnotherItem).
In order to understand why you get messages in that order, we need to know what url are you calling to generate that message.
In any case though, "/api/getItem" does not call "/api/getAnotherItem" for two reasons:
if you call next in "/api/getItem", it will call the next matching route, in this case, it will match either a route on "/api", "/" or nothing at all. the next() function basically calls the route's "parent".
Next() must be called explicitly, if you do not return an answer, express will simply wait indefinitely until res.send is called (which is how it can handle async functions).
It's possible you have some kind of middleware (I.E. used with app.use and not app.get) which could call something like that, but most likely, you are calling both urls at the same time somehow.
Related
I have been looking through some code online for building a React to-do app that uses an Express backend. The link to the website is here, and I came across this part of the code:
app.get("/todos", async (req, res, next) => {
try {
const todos = await db.Todo.find({});
return success(res, todos);
} catch (err) {
next({ status: 400, message: "failed to get todos" });
}
});
I know that the next function is a function that passes the operation of the current middleware function that it is in to the next middleware function of the same route. However, sources online just use the simple "next()" function, but this code has a value, an object, that is passed into the next function.
What does this mean?
this code has a value, an object, that is passed into the next function. What does this mean?
Ans: This means that you are passing an object as a parameter to the next middleware function.
app.use((err, req, res, next) => {
return res.status(err.status || 400).json({
status: err.status || 400,
message: err.message || "there was an error processing request"
});
});
Here err parameter is the object that you have passed.
Hope this helps
It seems to be a naming convention in Node.js, used to control the next matching route.
This stuff is frequently found, also very handy, and mostly used in access checks or wildcard routes. (/user/:id)
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in. If not, redirect to login.
if (!store.getters.isLoggedIn) {
next({
path: `/${ViewName.UserLogin}`,
query: { redirect: to.fullPath }
});
} else {
next();
}
}
From the express documentation:
Starting with Express 5, middleware functions that return a Promise will call next(value) when they reject or throw an error. next will be called with either the rejected value or the thrown Error.
So it seems to me like the value inside the next() function is the return value that is sent to the next callback. Often you don't want to send a custom value and just go to the next middleware function, however in this case they apparently wanted to set the error message inside the next() function and thus override any default values.
Hope this helps
I have API to deal with post request as follow(simplified):
myFunc(req: express.Request, res: express.Response, next){
let err = 'err detected!';
//validateSometing() returns a boolean value, true if validation pass false otherwise
if(!validateSomething()){
res.status(500).json(err);
return next(err);
}
//more code...logic if validation pass
}
I would like to know if return next(err); or return; is required to stop the function flow after sending the status and related err back to client. In other words, does res.status(500).json(err); stops the function flow?
Thanks!
next() is a middleware function in the application’s request-response cycle. You must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.
res.json(), res.send() is a express function used to send response to the client application . In other words it used this functions used to build your HTTP Reponse.
return keyword returns from your function, thus ending its execution. This means that any lines of code after it will not be executed.
Note : Both next() and res.send() will not end your function from execution. Where adding a return will stop function execution after triggering the callback.
Use return is to ensure that the execution stops after triggering the callback. In some circumstances, you may want to use res.send and then do other stuff.
Example :
app.use((req, res, next) => {
console.log('This is a middleware')
next()
console.log('This is first-half middleware')
})
app.use((req, res, next) => {
console.log('This is second middleware')
next()
})
app.use((req, res, next) => {
console.log('This is third middleware')
next()
})
Your output will be:
This is a middleware
This is second middleware
This is third middleware
This is first-half middleware
That is, it runs the code below next() after all middleware function finished.
However, if you use return next(), it will jump out the callback immediately and the code below return next() in the callback will be unreachable.
I'm learning express from various tutorials and have an app working locally, but I'd like to better understand what each part of the code does.
I'm a bit stumped with the example in the app.route() section here:
https://expressjs.com/en/guide/routing.html
app.route('/book')
.get(function (req, res) {
res.send('Get a random book')
})
.post(function (req, res) {
res.send('Add a book')
})
.put(function (req, res) {
res.send('Update the book')
})
I can see that app is equal to express(), which is a top level function documented here.
And I can see that the .get(), post() and put() methods are chained to the route() method, which is documented here.
Where I get confused is that the docs state that the arguments for the .get(), post() and put() methods are in this format:
app.get(path, callback [, callback ...])
app.post(path, callback [, callback ...])
app.put(path, callback [, callback ...])
Why do the chained .get(), post() and put() methods not require the path argument, and instead have a singular function as an argument that returns values from the Request (aka req) and Response (aka res) object parameters?
I'm obviously missing something simple, so pointers to documentation that could help me better understand the distinctions between these methods when called straight from app, eg app.get(), and from route(), eg app.route('/book').get() would be much appreciated.
Edit: Basically, I'd like to know if there is documentation that defines the required argument format for the .get(), post() and put() methods when called from the route object returned from calling app.route("/book"), because it does not seem to be what is documented, ie path, callback [, callback ...].
app.route()
As per the docs, the app.route method:
Returns an instance of a single route, which you can then use to handle HTTP verbs with optional middleware. Use app.route() to avoid duplicate route names (and thus typo errors).
It means, app.route() takes only the path and returns the route object. Which will have all http verb methods to handle middlewares against one path, get, post, delete, post, put, patch etc.
Why?
To simply have routes which have same path but different HTTP requests. Like:
app.route('/books')
.get() // To get the list of objects
.post() // To save a new book.
Individual HTTP methods
On the other hand, express provides individual methods on app for handling HTTP requests. Like app.get(), app.post(), app.delete().
As per docs for post route: HTTP POST requests to the specified path with the specified callback functions.
Why?
For the cases where you don't have one path for multiple HTTP requests. Let's say:
app.delete('/books/:bookId/comments/:commentId', function(){});
The above route is a kind of single route and only used for deleting a specific comment on book.
I hope I was able to clear the difference.
Reference Link for the docs: https://expressjs.com/en/4x/api.html#router.route
Edit:
As no proper Docs are available listing methods provided by route object:
For more information adding github's link to the express router.
https://github.com/expressjs/express/blob/master/lib/router/route.js
Here see the below code of express's router which adds handler on all methods.
methods.forEach(function(method){
Route.prototype[method] = function(){
var handles = flatten(slice.call(arguments));
for (var i = 0; i < handles.length; i++) {
var handle = handles[i];
if (typeof handle !== 'function') {
var type = toString.call(handle);
var msg = 'Route.' + method + '() requires a callback function but got a ' + type
throw new Error(msg);
}
debug('%s %o', method, this.path)
var layer = Layer('/', {}, handle);
layer.method = method;
this.methods[method] = true;
this.stack.push(layer);
}
return this;
};
});
In this file at top, it has:
var methods = require('methods');
methods: https://github.com/jshttp/methods
Thus, the required parameters for the chained methods is unlimited functions as request handlers/middlewares.
The point of chained method is that they have same path.
So you can write this:
app.route('/book')
.get(function (req, res) {
res.send('Get a random book')
})
.post(function (req, res) {
res.send('Add a book')
})
.put(function (req, res) {
res.send('Update the book')
})
instead of
app.get('/book', function (req, res) {
res.send('Get a random book')
});
app.post('/book', function (req, res) {
res.send('Add a book')
});
app.put('/book', function (req, res) {
res.send('Update the book')
});
Which means that if you i.e. change the endpoint it is changed for all methods, you cannot write typo in one method...
I try to execute two functions in a post request but only one is triggered. The function makeEntry executes but the function afterwards renderEntries does'nt. Can anyone help pls?
Here is the code: https://codepaste.net/bpsxsy
This is due to how callbacks work in Javascript. Here is the part which matters:
app.post('/guestbook_post', urlencodedparser, function(req, res){
makeEntry(req, res, Guestbook);
renderEntries(res, Guestbook);
});
You pass res to both functions. But makeEntry invokes res.send() which means it will end the request and send the response back to the client. However, renderEntries was not yet executed, but still waiting in event loop. It will be executed the next time you make a post request, which will lead to very confusing and buggy behaviour.
To make it work as intendeed, refactor functions makeEntry and renderEntries to return the needed result object and render it to the client once. Something like this:
app.post('/guestbook_post', urlencodedparser, function(req, res){
makeEntry(req, Guestbook, (err, entry) => {
renderEntries(entry, Guestbook, (err, result) => {
res.send(result);
});
});
});
I currently have a POST route defined in an Express Node.js application as so:
var locationService = require("../app/modules/locationservice.js");
app.post('/createstop', isLoggedIn, function(req, res) {
locationService.createStop(res, req.body);
});
(for this question, please assume the routing in & db works.. my record is created on form submission, it's the response I am struggling with)
In the locationservice.js class I then currently have
var models = require('../models');
exports.createStop = function(res, formData) {
models.location.build({ name: formData.name })
.save()
.then(function(locationObj) {
res.json({ dbResult : locationObj });
});
};
So as you can see, my route invokes the exported function CreateStop which uses the Sequelize persistent layer to insert a record asynchronously, after which I can stick the result on the response in the promised then()
So at the moment this only works by passing the response object into the locationservice.js method and then setting res.json in the then() there. This is sub-optimal to me with regards to my service classes, and doesn't feel right either.
What I would like to be able to do is "treat" my createStop method as a promise/with a callback so I can just return the new location object (or an error) and deal with it in the calling method - as future uses of this method might have a response context/parameter to pass in/be populated.
Therefore in the route I would do something more like:
var locationService = require("../app/modules/locationservice.js");
app.post('/createstop', isLoggedIn, function(req, res) {
locationService.createStop(req.body)
.then(dataBack) {
res.json(dataBack);
};
});
Which means, I could call createStop from else where in the future and react to the response in that promise handler. But this is currently beyond me. I have done my due diligence research, but some individual expert input on my specific case would be most appreciated.
Your locationservice.js could look like that
exports.createShop = function(data){
// here I have used create instead of build -> save
return models.location.create(data).then(function(location){
// here you return instance of saved location
return location;
});
}
And then your post() method should be like below
app.post('/createstop', isLoggedIn, function(req, res){
locationService.createShop(req.body).then(function(location){
// here you access the location created and saved in createShop function
res.json(location);
}).catch(function(error){
// handle the error
});
});
Wrap your createStop function with a promise like so:
exports.createStop = function(res, formData) {
return new Promise(function(resolve, reject) {
models.location.build({ name: formData.name })
.save()
.then(function(locationObj) {
resolve({ dbResult : locationObj });
});
//in case of error, call reject();
});
};
This will allow you to use the .then after the createStop within your router.