What's the purpose of multiple root handlers? - javascript

As stated, multiple callback functions can be provided and behave like middleware to handle a request. They can be in the form of a function, an array of functions, or combinations of both, as shown in the following examples.
For example:
app.get('/example/b', function (req, res, next) {
console.log('the response will be sent by the next function ...')
next()
}, function (req, res) {
res.send('Hello from B!')
})
What's the purpose of this? Can't we simply use:
app.get('/example/b', function (req, res) {
console.log('the response will be sent by the next function ...')
res.send('Hello from B!')
})

The multiple functions would more likely be used when you already have a previously defined function that you probably intend to use in multiple places. For example:
app.get("/somePath", checkAuth, function(req, res) {
// you know it's already authenticated here
});
app.get("/someOtherPath", checkAuth, function(req, res) {
// you know it's already authenticated here
});
function checkAuth(req, res, next) {
if (some logic here) {
// allow handler chain to continue
next();
} else {
// auth error
res.status(401).end();
}
}
Of course, you could also use middleware for checking authentication, but the above example allows you to target just a few specific routes with some middleware that you may use in multiple places.
As you have already observed, if you don't intend to use the function anywhere else, then you may as well just put the logic into your one handler.

Yes you can, the purpose is for example, to handle errors, the middleware sequence in express allows you to use this way. For example, see this way to set up the express config:
app.use(logger.connectLogger());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(routes);
app.use(errorConnect);
http.createServer(app).listen(config.port, function () {
logger.getLogger().info('My backend listening on port: ' + config.port);
});
My routes module have all the matching route -> callback:
// Methods exposed for backend API.
router.get('/status', ips.getStatus);
router.route('/ip')
.post(ips.keepIps)
.get(ips.getIps)
// NOT ALLOWED
.put(returnNotAllowed)
.delete(returnNotAllowed);
// Methods exposed and used by IP's frontend.
router.route('/front/ip')
.get(front.getIpsByFront)
.post(front.keepIpsByFront);
router.post('/login', login.login);
....
For example in one of those callbacks, I have the next way to manage an incoming request:
/**
* Login user from frontend.
*/
exports.login = function(req, res, next) {
var username = req.body.username + '#System',
password = req.body.password,
server = req.body.server,
auth = 'Basic ' + new Buffer(username + ':' + password).toString('base64');
loginApi.login(auth, server)
.then(function(result) {
res.statusCode = 200;
res.send(result);
})
.catch(function(error) {
next({
statusCode: 403,
message: 'Error in login'
});
});
};
When I catch an error, I call next with a custom error object, and after this, if you back and watch the config (first paragraph) you can see that I added to the express middleware an error manage with errorConnect. In my opinion this is a usefull way to understand what you are asking because if I understand well you had doubts with next()

Related

How should a server implement middlewares chaining?

I'm struggling a lot trying to understand the logic behind the ExpressJS module, in particular i'm focusing on the implementation of the middlewares chain.
My objective here is to understand how is it possible to implement the logic of a server listening for requests, and once arrived, passes the request packet through an array of functions each of which:
should be able to modify the packet for the next middleware
should be able to send the response back but it should not prevent other middlewares to respond as well, so there should be only one response at the end of the chain (i guess)
should access some function to call in order to run the next middleware in chain, but without the need to pass it the request and response parameters (e.g. just calling "next( )" )
Note on point 1
I imagine that with languages like javascript is possible to pass the object around using concepts like closure, but i would like to understand it in a language-independent way and use javascript features only if it provides clever ways to handle it simply, otherwise i'll write the logic as i was using any other language.
So my big questions are:
How should the request handling be implemented?
How do i allow every middleware to modify the packet?
How do i pass the updated packet to the next middleware?
How do i implement the "next" function to pass to every middleware?
Thank you
Broadly, this is how a chain of middlewares with a next function can be called.
function myMiddleware(req, res, next) {
// do something with req or res
next();
}
const mws = [
myMiddleware,
anotherMiddleware
];
/**
* Calls a chain of middlewares.
*
* mws is an array of middlewares
*/
function callMwChain(req, res, mws) {
if (mws.length === 0) {
// We're done here
return;
}
/**
* Take the first middleware
*/
const firstMw = mws[0];
/**
* Call it, and give it a next function that continues the chain
*/
firstMw(req, res, () => {
callMwChain(req, res, mws.slice(1));
});
}
I've written this as a recursive function, but it can also be rewritten as stack. This is easier though.
Express middlewares have a lot of bonus magic, so their implementation is going to be more complex, but this is broadly how it works.
Note that there's no event-loop in sight.
Here's a little working server that supports middleware and GET routes. It uses a similar calling convention on the route handlers as Express (req, res, next), but has far, far, far fewer features than Express. But, hopefully you can see how the dispatch function cycles through the routes, advancing to the next route only if the previous handler calls next().
You can actually run this code and define routes and play with it (though it is definitely barebones basic):
const http = require('http');
function dispatch(req, res, array, index, cb) {
console.log(req.method, req.url, array, index);
if (array && array.length > index) {
let item = array[index];
if (!item.path || item.path.toLowerCase() === req.url.toLowerCase()) {
item.fn(req, res, function() {
// previous route called next(), so we advance to the next item in the array
++index;
dispatch(req, res, array, index, cb);
});
return;
}
}
cb();
}
const server = http.createServer((req, res) => {
// incoming request here, initiate calling the middleware
dispatch(req, res, server.middleware, 0, function() {
if (req.method === "GET") {
dispatch(req, res, server.gets, 0, function() {
// done with gets here, apparently didn't send a response
res.statusCode = 404;
res.end();
})
} else {
// ... fill in code for other methods here
res.statusCode = 404;
res.end();
}
});
});
// server route implementation
// save arrays for each type of route handler
// upon a request, run all the middleware requests, then advance
// to the routes for the right type of request and see if any of them
// match the URL. If so, run them.
server.middleware = [];
server.gets = [];
server.posts = [];
server.use = function(fn) {
server.middleware.push({path: "", fn: fn});
}
server.get = function(path, fn) {
server.gets.push({path, fn});
}
// route definitions
server.use((req, res, next) => {
console.log(req.url);
req.myCustom = "hi";
next();
});
server.use((req, res, next) => {
console.log(req.myCustom);
next();
});
server.get("/", (req, res, next) => {
res.write("Hello World");
res.end();
});
server.listen(80);

req.getConnection is not a function in express-myconnection

i am trying to connect mysql, but it is showing me req.getConnection is not a function
Bellow is the code snipet which i have used.
In app.js
var mysql = require('mysql'),
connection = require('express-myconnection'),
dbOptions = {
host: 'localhost',
user: 'root', password: '',
port: 3306,
database: 'nodejs'
};
app.use(connection(mysql, dbOptions, 'request'));
In Customers.js
exports.list = function(req, res, next) {
req.getConnection(function(err, connection) {
if (err) return next(err);
connection.query('SELECT * FROM customer', function(err, rows) {
if (err) console.log("Error Selecting : %s ", err);
res.render('customers', {
page_title: "Customers - Node.js", data: rows
});
});
});
};
Can you please check what is have done wrong. I am pretty new in nodejs, so may be i am missing something.
If you want to check my complete package, please follow bellow URL, i have push all code in my git repository
https://github.com/chiraggmodi/nodeCrud
You need to move this line:
app.use(connection(mysql, dbOptions, 'request'));
Right now, it's declared after your routes, which means that requests will never pass through it (that's how Express works: requests are passed through both middleware and route handlers in order of their declaration).
If you use it to a location in the code before the routes that require req.getConnection() to work (perhaps here), requests will first pass through the express-myconnection middleware and then get passed to the route handlers, making sure that req.getConnection() will be available inside those route handlers.
EDIT: on closer look, your customer routes are also incorrect.
You have this:
router.get('/', function(req, res, next) {
res.render('customers', customers.list);
});
But customer.list is a route handler in itself, not something you should pass to res.render(). Instead, try this:
router.get('/', customers.list);
(and similarly for all the other routes in customers_route.js)

Returning HTML or JSON based on header type Nodejs

My nodeJS Api needs to return HTML or Json based on the header. If the header is:
Accept = application/json
The Api needs to return Json else my Api needs to return a HTML file.
this is the code I use on my routes:
var app = express();
app.use('/auth', require('./routes/Authenticate'));
In the Authenticate file I catch /login, and do the login stuff. if it succeeds I redirect to /users. In the /users I check for the Accept with an if statement:
router.get('/users', function(req,res){
if(req.get('Accept') === 'application/json'){
res.json({ success: true, user: req.user });
} else {
res.render("account/profile") //redirect to the file
}
});
This works(from this solution) but is there a better way? Because there are like 20 endpoints and the application is growing and this will be a mess for every endpoint.
you can split these actions into 2 functions. One to verify the content type and an other to doing your actions.
router.get('/users', checkIfJson, action);
function checkIfJson(req, res, next) {
if(!(req.get('Content-Type') === 'application/json')) {
res.render("account/profile");
return;
}
next();
}
function action(req, res) {
res.json({ success: true, user: req.user });
return;
}
If you write your code like that you can reuse your checkIfJson into other routes.
You can wrap router.get function with a custom function
router.wrappedGet = function (path, callback) {
router.get(path, function (req, res) {
if (req.get('Content-Type') === 'application/json') {
res.render = res.json;
}
callback(req, res);
})
};
Here's what I've doneā€”seems pretty straightforward.
router.get("/foo", HTML_ACCEPTED, (req, res) => res.send("<html><h1>baz</h1><p>qux</p></html>"))
router.get("/foo", JSON_ACCEPTED, (req, res) => res.json({foo: "bar"}))
Here's how those middlewares work.
function HTML_ACCEPTED (req, res, next) { return req.accepts("html") ? next() : next("route") }
function JSON_ACCEPTED (req, res, next) { return req.accepts("json") ? next() : next("route") }
Personally I think this is quite readable (and therefore maintainable).
$ curl localhost:5000/foo --header "Accept: text/html"
<html><h1>baz</h1><p>qux</p></html>
$ curl localhost:5000/foo --header "Accept: application/json"
{"foo":"bar"}
Notes:
I recommend putting the HTML routes before the JSON routes because some browsers will accept HTML or JSON, so they'll get whichever route is listed first. I'd expect API users to be capable of understanding and setting the Accept header, but I wouldn't expect that of browser users, so browsers get preference.
The last paragraph in ExpressJS Guide talks about next('route'). In short, next() skips to the next middleware in the same route while next('route') bails out of this route and tries the next one.
Here's the reference on req.accepts.

How to create a reusable function in Node without writing boilerplate code

I am using Express.js as http server. Defined all my routes.
Most endpoints need to verify session before returning a response. E.g. below code serves users in the system and list of services respectively:
function getUsers(req, res, next) {
verifyUser(req, res, next, function () {
//serve users
});
}
function getServices(req, res, next) {
verifyUser(req, res, next, function () {
//serve services
});
}
You probably noticed there is a verifyUser function which validates the session. Which is as below.
function verifyUser(req, res, next, callback) {
var sessionKey = req.cookies.sessionKey;
var user = users.userBySession(sessionKey);
if (user) {
callback(req, res, next, user);
} else {
res.status(401).send({
message: 'Unauthorized'
});
}
}
As you can see I keep passing in req, res and next parameters along with a callback whenever I use this function.
I tried to use apply function to make it easier. Changed my getUsers function like this:
function getUsers(req, res, next) {
verifyUser
.apply(null, arguments, function () {
//serve users
});
}
The problem with this approach is callback is not passed into verifyUser function. And I don't really like passing null as scope with each call.
How can I achieve this by writing less and better code ? Any ideas?
You could use bind to create a 'partial function':
// create bound responseHelper object
var responseHelper = verifyUser.bind(null, req, res, next);
// usage
responseHelper(getUsersCallback); // same as verifyUser(req, res, next, getusersCallBack);
I think you're looking to turn verifyUser into a middleware function.
function verifyUser (req, res, next) {
var user = // yadda yadda session stuff
if (user) {
req.user = user; // [1] what you do to the req object here...
} else {
return res.status(401).send({ message: "No way Smokey Joe"});
/**
* alternatively, do something like
* var err = new Error("Not authorized");
* err.statusCode = 401;
* return next(err);
*
* this will kick off Express' error handling mechanism,
* which you should read about in the docs (see the link below)
*/
}
next();
// very important to call next after this verifyUser has done its job
// if you don't, the next middleware won't go off,
// and the request will just hang
}
function getUsers (req, res, next) {
// [2] will show up on the req object here, assuming you chain these
// two functions together as middleware
}
app.get("/users", verifyUser, getUsers);
app.get("/services", verifyUser, getServices);
// here's a route that needs no session auth, so no need to verifyUser
app.get("/latest-posts", getLatestPosts);
When you tell Express to use a function or attach a function to a route path via get('/my/route', hanlderFun) or some such, you've basically turned handlerFun into a middleware.
You can define however many middleware as handlers on a route as you like, and they'll all execute in turn as long as you keep calling next.
app.post("/checkout", verifyUser, tallyCart, checkInventory, doPayment, sendInvoice);
The job of next is to pass control from the current middelware to the next one. It's an object
You can do other stuff with next, too, which you should read up on in the docs.
http://expressjs.com/en/guide/writing-middleware.html
http://expressjs.com/en/guide/using-middleware.html
The docs on routing have good info on middleware as well:
http://expressjs.com/en/guide/routing.html
For extra credit, check out error handling middleware, too:
http://expressjs.com/en/guide/error-handling.html

Need help on how to actually use Passport authentication strategy in Express

Suppose I have a script like this, which uses a Passport authentication strategy with an Express backend. How would I use this script to actually make API function calls? I don't see any explicit examples in the linked project's documentation nor can I find anything in Passport.js's documentation. Thanks.
After passport user serialization done, every request has user field, which contains information passed to done callback of passport.serializeUser method.
app.get('/userID', function (req, res) {
if (req.isAuthenticated()) {
res.json(req.user.id);
}
res.redirect('/login');
}
Also, you have access to session
app.get('/auth/fitbit/callback',
passport.authenticate('fitbit', { failureRedirect: '/login' }),
function(req, res) {
req.session.loggedInAt = Date.now();
res.redirect('/');
});
Information stored in session available in all requests, while user is authenticated
app.get('/someroute', function (req, res) {
// call to another service
var url = 'http://superservice.com/' + req.user.id + '/' + req.session.loggedInAt
http.get(url, function (_res) {
res.send(_res.data)
});
});
I'm supposing that you know how to use passport, and you will figure it out what's the right Fitbit API endpoint (honestly, I'm don't know it). Said that, let me give an idea that might help you solve your problem:
// An awesome npm module (https://github.com/mikeal/request)
var request = require('request');
//
//
//
// An express route.
app.get('/activities', function (req, res) {
if (req.user !== null) {
// User is authenticated.
getUserActivities(req.user.id, res);
} else {
// Redirect to login the user isn't authenticated.
res.redirect('/login');
}
});
// This function will make API calls to Fitbit
// using the User ID we got from the PassportJS
// authentication process.
function getUserActivities(id, res) {
// It will request from Fitbit User activities.
request('https://api.fitbit.com/1/user/'+ id +'/activities/',
function (error, response, body) {
if (!error && response.statusCode == 200) {
// If everything goes well.
return res.send(body);
} else {
// If something wrong happens.
return res.send(error);
}
);
}
The goal of this example is to show you that you need to use PassportJS to get fitbit users ID, then use that id to make API calls to fitbit.

Categories

Resources