example: public api doesn't require authentication, private route should pass through middleware authentication. But how do we differentiate incoming http request is public or private.
If you're using Express, there are many choices. A simple design choice woudl be to put your private APIs on a router that has middleware that checks for authentication before routing to any of the routes and put the public APIs on a router that does not have the middleware check. Then, the router mechanics built into Express and a properly placed middleware on the router that requires auth would do all the work for you.
Here's a simple example:
const express = require('express');
const app = express();
const routerAuth = express.Router();
const routerPublic = express.Router();
// check auth here in the routerAuth router as the first route definition in that router
routerAuth.use((req, res, next) => {
// check auth here
if ( /* code here to check authorization */) {
next();
} else {
// not authorized
res.status(401).send("Not authorized");
// or maybe just res.redirect("/login");
}
});
// routers on routerAuth
routerAuth.get("/seeEverything", ...)
routerAuth.get("/admin", ...)
// routes on routerPublic
routerPublic.get("/", ...);
routerPublic.get("/login", ...);
routerPublic.get("/signup", ...);
// hook the routers into our server
// specify the public router first so they get a chance to be matched
// before routerAuth enforces authentication
app.use(routerPublic);
app.use(routerAuth);
app.listen(80);
It would be common to put each of these routers in their own module and then import them, but I've skipped that part for simplicity's sake here. This example shows each router sharing the same path namespace, but you could also put the private router on its own path prefix if you want.
Related
I'm very new to express and am running into issues getting proper routing set up. It's a homework assignment so the router file was already written, but there's a express.js file we're supposed to fill in to make calls to the api whenever a get/put/post/delete request is made at that address. The router file is set up like this:
var listings = require('../controllers/listings.server.controller.js'),
getCoordinates = require('../controllers/coordinates.server.controller.js'),
express = require('express'),
router = express.Router();
/*
These method calls are responsible for routing requests to the correct request handler.
Take note that it is possible for different controller functions to handle requests to the same route.
*/
router.route('/')
.get(listings.list)
.post(getCoordinates, listings.create);
/*
The ':' specifies a URL parameter.
*/
router.route('/:listingsId')
.get(listings.read)
.put(getCoordinates, listings.update)
.delete(listings.delete);
/*
The 'router.param' method allows us to specify middleware we would like to use to handle
requests with a parameter.
Say we make an example request to '/listings/566372f4d11de3498e2941c9'
The request handler will first find the specific listing using this 'listingsById'
middleware function by doing a lookup to ID '566372f4d11de3498e2941c9' in the Mongo database,
and bind this listing to the request object.
It will then pass control to the routing function specified above, where it will either
get, update, or delete that specific listing (depending on the HTTP verb specified)
*/
router.param('listingId', listings.listingByID);
module.exports = router;
And the express.js file is like this:
var path = require('path'),
express = require('express'),
mongoose = require('mongoose'),
morgan = require('morgan'),
bodyParser = require('body-parser'),
config = require('./config'),
listingsRouter = require('../routes/listings.server.routes'),
getCoordinates = require('../controllers/coordinates.server.controller.js');
module.exports.init = function() {
//connect to database
mongoose.connect(config.db.uri, {useMongoClient: true});
//initialize app
var app = express();
//enable request logging for development debugging
app.use(morgan('dev'));
//body parsing middleware
app.use(bodyParser.json());
/* server wrapper around Google Maps API to get latitude + longitude coordinates from address */
app.post('/api/coordinates', getCoordinates, function(req, res) {
res.send(req.results);
});
This is the part I can't figure out:
/* serve static files */
app.get('/listings', listingsRouter, function(req, res){
res.send(req.get('/'))
});
/* use the listings router for requests to the api */
/* go to homepage for all routes not specified */
return app;
};
I'm just not sure how to use the routes in the listingsRouter file with the req and res objects and I can't find any examples of a program set up like this to help. Any assistance would be appreciated.
Change following
app.get('/listings', listingsRouter, function(req, res){
res.send(req.get('/'))
});
To
app.use('/listings', listingsRouter);
Express Router. Scroll down to express.Router section for complete info.
Hope this helpls.
I have a middleware, I call it like this:
app.use(example({optiona:'abc'}));
I want to access the app from the middleware function, and do another app.use, something like this:
module.exports = function (options){
app.use(...);
return function(req, res, next)
next();
}
I know the option to pass the app to the exports, but I want to do it without the option to pass it, or set it as global..
You could try looking into Express Routers as an option (read all about it).
Essentially, you could keep your first piece the same:
app.use(example({optiona:'abc'}));
And then you can do the following in the function instead:
var express = require("express");
var router = express.Router();
module.exports = function(options) {
// You can declare middleware specific to this router
router.use(...);
// You can declare routes specific to this router
router.get("/foo", ...);
router.all("/bar", ...);
// Then just return the router at the end
return router;
}
Routers allow you to set up "sub applications" with their own routes/middleware accordingly.
I have developed an API using Node.js + Express JS and i use token-based authentication.
I used two differents routers in this api, userRoute (/USER) and postRoute (/POST). The postRoute can be used within authentication but userRoute needs the token.
To solve that i use a router middleware for userRoute but it interferes with portRoute
This is the code:
...
var postRoute = express.Router();
var userRoute = express.Router();
// Route middleware to verify a token
userRoute.use(function(req, res, next) {
security.checkToken(req,res, next);
});
userRoute.route('/users')
.get(userCtrl.findAllUsers)
.post(userCtrl.addUser);
postRoute.route('/posts')
.get(userCtrl.findAllPosts)
.post(userCtrl.addPost);
app.use(userRoute);
app.use(postRoute);
...
If i try to access '/posts' the servers checks the token and does not let me in. I know if i change the order of app.use it works, but i dont understand why works in this way if i am using "Router Middleware".
Someone knows?
This happens because if the express router implementation, you will be able to understand it quite easily if you have a look at it. Here is the path: node_modules/express/lib/router/index.js. Every time that you call the Router(), like in your case
var postRoute = express.Router();
var userRoute = express.Router();
this function will be invoked:
var proto = module.exports = function(options) { ... }
and it is true that there is a different router instance that is being returned every time. The difference is in the way that the use is registering the middlewares. As you see the use it is registered against the proto.use
proto.use = function use(fn) { ... }
this means that the middleware that you register there they will be registered for every instance of the router that you define.
I'm developing part of a system where we have two applications sharing the same domain so nginx makes exampleurl.com go to one application and example.com/admin/* go to the second.
The /admin/* part is going to a NodeJs app using express.
Is there an elegant way of making sure that node can add in the /admin without having to do
app.get('/admin/endpoint', ...)
?
you can use http://expressjs.com/fr/api.html#router
var router = express.Router();
router.get('/', function(req, res) {
res.send('One page in admin website');
});
// router.get('/adminWebsite'); // idem for all routes(you always use this router)
app.use('/admin', router);
you would would to use routers (preferably) but you can also create another express app. using different Apps allow you to have more global middlewear control whereas using different routes mean that they share the same express instance.
var express = require('express');
var admin = express(); // <- this is now your admin application
var app = express(); // <- this is your main applicaiton regular users
//you will need to set up middlewear like body-parser and stuff for both of them now
//but it allows you to use different logging and authentication system or w.e you
//want
//once everything is done you can 'MOUNT' virtually the admin app to the regular app
app.use('/admin', admin); //<- this will nest apps together but allow the sub-app admin
//to be it's own instance.
//app.js
const admin = require('admin'); //you admin module (where you routes wiil be)
app.use('/admin', [functions... example authCheck], admin);
//index file of admin module
`var express = require('express');
var router = express.Router();
router.use(function authCheck(req, res, next) {
//check if user is logged
});
router.get('/main', require('./main').get);
module.exports = router;`
//main.js
`exports.get = function(req, res, next){
res.render('admin/admin', {
title: "Main panel"
});
};`
and now you can access site.com/admin/main
I know I can get the Express app from inside an individual route with:
req.app
However I need to start a single instance of a module inside routes/index.js, i.e.:
var myModule = require('my-module')(propertyOfApp)
How can I get the express app from the router?
It really depends on your own implementation, but what I suggested in the comments should be working:
// index.js
module.exports = function(app) {
// can use app here
// somehow create your router and do the magic, configure it as you wish
router.get('/path', function (req, res, next) {});
return router;
}
// app.js
// actually call the function that is returned by require,
// and when executed, the function will return your configured router
app.use(require('./index')(app));
p.s.
Of course this is just a sample - you can configure your router with path, and all kind of properties you wish. Cheers! :)