Reputation: 2254
I'm new to Node and Express and the other layers that go along with building web apps with node and the request
and response
parameters are really confusing me. My confusion lies in the fact that those two parameters are often present in a function but oftentimes one or both of them isn't declared. Also, much of the time an additional parameter will be thrown in, like 'next'
or something else. For example, I have the following router for an API:
router.route('/teams')
// create a team at POST http://localhost:8080/api/teams
.post(function(req, res) {
var team = new Team();
team.name = req.body.name;
team.location = req.body.location;
// save the new team and check for errors
team.save(function(err) {
if (err) {
res.send(err);
};
res.json({ message: 'Team created!' });
});
})
// GET all the teams at GET http://localhost:8080/api/teams
.get(function(req, res) {
Team.find(function(err, teams){
if (err) {
res.send(err);
};
res.json(teams);
});
});
Both .post
and .get
call a function with req
and res
as parameters, but req
is never used. So how does the function know what to do with req or res if they're not defined and used or not used in completely different orders? Or if I named them something completely different?
What exactly is happening with requests and responses? Sorry for my ignorance. I've read the documentation but it's not clicking.
Thanks.
Upvotes: 5
Views: 12211
Reputation: 19569
When you use expressApp.use('/some/route', myRouteHandler);
Express will listen for requests for that route, and when it's hit, it will call the function you provided (callback). It will give it three parameters: request and response, and next. (Actually could be four, but lets keep things simple.)
So, your callback might be defined like this:
function myRouteHandler(a, b, c) {
// do stuff
};
or like this:
function myRouteHandler(req, res, next) {
// stuff
}
or simply:
function myRouteHandler() {
// stuff
}
Whatever you do, doesn't matter. When the app is started, express listens for requests.
When one of them matches the route (/some/route
), express will, in its internal workings, call the function you provided, like this:
myRouteHandler(requestObject, responseObject, nextMiddleware);
So in the first case, you can access the request (like, request headers, full url, caller IP address or similar) by using req. In your second case, you'll access it by calling a. In the third case, you can use arguments[0].
By convention, people will use the form: myCallback(req, res)
and know that Express will put the request object as the first param, and response as the second. The response object actually has a method end(), so you can end the request. If there is also a next() object, you can call the next middleware.
Say you have a route defined like this:
app.use('/api/users', checkAuthorizationHandler);
app.use('/api/users', makeSureTheIPisFromOurInternalNetwork);
app.use('/api/users', nowHandleTheResponse);
Each of those handlers gets a third param. If you name it, you'd usually in your function declaration call it 'next' parameter. It means, the next function in order.
Say your function checkAuthorizationHandler(req, res, next)
will check for req.headers('auth') token and if it's ok, it will in the function body, call next()
.
Then the function makeSureTheIPisFromOurInternalNetwork(a, b, c)
is called. It will check that the a.ip is a LAN ip address and call c();
Finally your function nowHandleTheResponse()
will find all users, and respond with a JSON object of the users: arguments[1].json([user1, user2, user3]);
So, first param is something that express gives you, it's the request, second is response, third is a next middleware function in line. No matter what you call them, they are there.
P.S. You can also declare your middleware with four params:
function(error, req, res, next);
Express will actually check your function and if it finds that you have four params and not two or three, it will give you any errors thrown by the middleware that ran earlier in the chain. Meaning, if your checkAuthHandler says next(new Error('Not authorized'));, your next function might check for that error, and if it's present, not give results. Often however will the middleware which detects errors just res.end('some error message');
If I haven't confused you enough, just say, I've got more where this came from :)
Upvotes: 7
Reputation: 6899
Request
, response
and next
are passed to all middleware functions. The request
object contains information about the HTTP request
, and the response
object is used to handle the request
. The Expressjs documentation details these objects. The next()
call is used in something called the dispatcher, a middleware function may or may not call next()
depending on usage. Next
simply calls the following middleware.
Here is an example of using next()
:
function helloWorld(req,res,next){
console.log('Hello, world!');
next();
}
// This function doesn't call next(), therefore it will
// not call the subsequent middleware in the dispatcher
function goodbyeWorld(req,res,next){
console.log('Goodbye, world!');
}
app.use(helloWorld);
app.use(goodbyeWorld);
Output:
Hello, world!
Goodbye, world!
Now let's reorder the middleware
app.use(goodbyeWorld);
app.use(helloWorld);
Output:
Goodbye, world!
The helloWorld
function is not called. Notice the importance of middleware order and the next()
function call.
Upvotes: 1
Reputation: 1136
It is the framework convention. The first argument is the request
, the second is the response
. If you're declaring a middleware (.use
), the third argument is the next
middleware in the chain.
You can name these variables however you want, as long as you know the order. You could have something like: .post(function(a,b) {});
and then the request is represented by variable a
, and response by variable b
.
If, for whatever reason, you don't need the request, only the response, you still have to have a first argument, as the response is represented by the second argument.
In javascript, there's no method overload like in Java, for example (maybe here's where you getting the confusion from). A function is represented by its name, not how many arguments it takes. Here's a simple example:
function logToConsole(level, message) {
if (!message) {
message = level;
level = 'INFO';
}
console.log('['+level+']', message);
}
logToConsole('My message'); // prints out: "[INFO] My message"
logToConsole('WARN', 'My message'); // prints out: "[WARN] My message"
Did you notice how we defined a default value for level
, based on the existence of message
?
Hope this clarifies things a bit.
Upvotes: 4