I would like to protect some endpoints in my express app, I want to create something simple to manage if my app became a big app...now I'm doing something like this:
setProtected(router) {
const self = this;
router.use(this.auth);
...
}
setPublic(router) {
const self = this;
...
}
getRouter() {
const router = express.Router();
this.setPublic(router);
this.setProtected(router);
return router;
}
with:
auth(req, res, next) {
if(req.isAuthenticated()) {
console.log('req.isAuthenticated()', req.isAuthenticated());
return next();
}
return res.send(401);
}
the problem in this case is that is difficult maintain and it doesn't work well as if I have /:id in my publicRoute and for example /my-items in my protected route when I'm not logged and I try to reach /my-items I get the code of /:id.
Another idea was to create a json with the list of all my urls with same information like protected/not protected and eventual roles and then change auth with something like:
import urls from './urls';
auth(req, res, next) {
if (urls[req.url] == 'public') {
return next()
}
else if (urls[req.url] == 'protected' && req.isAuthenticated()) {
return next();
}
return res.send(401);
}
whats the best way for you?
You can chain middlewares:
eg.
const authenticate = (req, res, next) {
.. some auth logic
next();
}
app.use('/', main...
app.use('/profile', authenticate, otherMiddleware,
app.use('/admin', authenticate, isAdmin, otherMiddleware...
in your main file (server.js) import the routes and use the middleware there :)
server.js
const express = require('express')
const cors = require('cors')
const app = express()
// import admin routes
const adminRoute = require('./app/routes/admin.route.js')
// Add middleware for parsing URL encoded bodies (which are usually sent by browser)
app.use(cors())
// Add middleware for parsing JSON and urlencoded data and populating `req.body`
app.use(express.urlencoded({ extended: false }))
app.use(express.json())
// homepage route
app.get("/", (req, res) => {
res.json({ message: "Hello World" })
})
// restricted by middleware "isAdmin"
app.use('/api/v1', isAdmin, adminRoute)
app.listen(8008).on('listening', () => {
console.log('Server is running on 8008')
})
admin.route.js
const express = require('express')
const admin = require('../controllers/admin.controller.js')
const router = express.Router()
// get all admin users
router.get('/users', (req, res, next) => {
admin.getAdminUsers(req, res, next)
})
module.exports = router
Related
Anyone know the solution? When I console log the variable it shows undefined. I need to pass the req.body (name and email) element to second post route which is "/company" route. I have two forms and I want to pass the first form data to second form the "/company" route form. then the two form data will be stored in mongoDb. I tried everything but I got only undefined in second route the "/company" route
import express from "express";
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
//Assign the name and email to variable and sent to last route
const middleware = (req, res, next) => {
const { name, email } = req.body;
let nameNew = name;
let emailNew = email;
// add nameNew and emailNew to req object
// so that it can be access in other routes
req.nameNew = nameNew;
req.emailNew = emailNew;
// console.log(nameNew);
// console.log(emailNew);
next();
};
//Get name email from user input and sent it middleware
app.post("/", middleware, (req, res, next) => {
const { name, email } = req.body;
res.send("Home route");
});
//Get the name and email from middleware
app.post("/company", middleware, (req, res) => {
// console.log(nameNew);
// access req.body without middleware
// console.log(req.body);
// access req.body using middleware
console.log(req.nameNew);
console.log(req.emailNew);
res.send("Company route");
});
//Server running
app.listen(4000, () => {
console.log(`Server Started `);
});
When your request gets into your app.post("/") it sets the nameNew variable inside your middleware scope. You cannot access this variable in your other routes.
If you want to access variables shared from your middleware you can use res.locals : https://expressjs.com/en/api.html#res.locals
You can't pass data from route 1 to route 2 through middleware.
It passes through the middleware and sets the variables but on your res.send theses variable will disappear. These are just set for the "instance" of the incoming request and on res.send will be destroyed.
You would need to send back data to the front-end to pass it back to your API for route 2.
You can call functions from your route definition :
app.get('/getUserInfos', isLoggedIn, (req, res) => {
return getUserInfos(res);
});
This way you can process your data and save it however you want.
Also, looks like you can chain functions there : https://stackoverflow.com/a/62723674/12343548
I use req.app.locals to send the data in first route to second route. Anyone want to do this type of program in future Here is the solution.
const express = require("express");
const app = express();
const path = require("path");
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const middleware = (req, res, next) => {
const { name, email } = req.body;
req.app.locals.nameNew = name;
req.app.locals.emailNew = email;
next();
};
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname + "/index.html"));
});
app.post("/", middleware, (req, res) => {
const { name, email } = req.body;
res.send("Home Route");
});
app.get("/company", (req, res) => {
res.sendFile(path.join(__dirname + "/company.html"));
console.log(req.app.locals.nameNew);
console.log(req.app.locals.emailNew);
});
app.post("/company", (req, res) => {
console.log(req.app.locals.nameNew);
console.log(req.app.locals.emailNew);
res.send("Company Route");
});
//Server running
app.listen(4001, () => {
console.log(`Server Started `);
});
So on router level file, I need to protect almost all routes with at least one middleware, except for one route. So far I tried something like this:
// comments.js
const express = require('express');
const router = express.Router({ mergeParams: true });
const m = require('../middlewares');
// /api/:newsId/comments - preappended route
router.use((req, res, next) => {
if(req.path == '/' && req.method == 'GET') return next(); // doesn't work, even with next('route')
if(m.isLoggedIn) { // this doesn't work either
next();
}
})
router.get('/', (req, res) => {
// returns comments for :newsId, only this route doesn't need middlewares
})
// all below routes needs at least m.isLoggedIn
router.post('/', (req, res) => {
// creates a new comment
})
// ...
// bunch of other routes which needs middleware m.isLoggedIn and maybe some others
module.exports = router;
I know I can append middlewares on each route, but I am looking for better solution to use in router.use().
I have a middleware that I want to be applied only when the http method is post.
The following works fine, but I get the feeling there is a better way:
'use strict'
const express = require('express'),
router = express.Router()
router.use((req, res, next) => {
if (req.method === 'POST') {
// do stuff
}
return next()
})
module.exports = router
I'd like to do something like this, but it doesn't work:
'use strict'
const express = require('express'),
router = express.Router()
router.post((req, res, next) => {
// do stuff
return next()
})
module.exports = router
You can use * symbol:
const express = require('express')
const app = express();
app.post('*', (req, res, next) => {
console.log('POST happen')
next();
})
app.post('/foo', (req, res) => {
res.send('foo');
});
app.post('/bar', (req, res) => {
res.send('bar');
});
app.listen(11111);
This will respond with "foo" string on POST /foo and with "bar" string on POST /bar but always log "POST happen" to console.
We can group our routes like this in Laravel:
Route::group("admin", ["middleware" => ["isAdmin"]], function () {
Route::get("/", "AdminController#index");
Route::post("/post", ["middleware" => "csrf", "uses" => "AdminController#index");
});
Basically, all the routes defined in admin group gets the isAdmin middleware and group name automatically. For example, post endpoint listens to admin/post not /post
Is there any way to do the same thing with Express? It would be awesome because my Laravel routes used to be so clean, whereas my Express routes are a bit messy/duplicated.
This is my routes.js on Express at the moment.
app.get("/admin", [passportConfig.isAuthenticated, passportConfig.isAdmin], AdminController.index);
app.post("/admin", [passportConfig.isAuthenticated, passportConfig.isAdmin], AdminController.postIndex);
Thank you.
Since express 4 you can define and compose routers
const app = require('express');
const adminRouter = app.Router();
adminRouter.use(isAdmin);
adminRouter.get('/', admin.index); /* will resolve to /admin */
adminRouter.post('/post', csrf, admin.index); /* will resolve to /admin/post */
app.use('/admin', adminRouter);
Hope that helps!
Just use before of every group you want to do:
app.use('/admin', AdminMiddleware);
app.get('/admin/route1', ...
app.get('/admin/route2', ...
app.use('/user', UserMiddleware);
app.get('/user/route1', ...
app.get('/user/route2', ...
require('express-group-routes');
var app = require('express');
app.group("/api/v1", (router) => {
router.get("/login", loginController.store); // /api/v1/login
});
In case you don't want to add a prefix but still need to group certain routes you can leave the first parameter and go straight for the function:
require('express-group-routes');
var app = require('express');
app.group((router) => {
router.use(middleware);
});
You can use app.use() - https://expressjs.com/en/guide/using-middleware.html#middleware.application
app.use("/admin",[passportConfig.isAuthenticated, passportConfig.isAdmin],AdminController)
// AdminController:
var express = require('express');
var router = express.Router();
router.get('/', AdminController.index);
// etc...
module.exports = router
https://expressjs.com/en/guide/routing.html#express-router
I found some better solution you can follow this method it's working good
Route file route/user.js
var express = require('express')
var router = express.Router()
const authMiddleware = require('../middleware/auth')
express.application.prefix = express.Router.prefix = function(path, middleware, configure) {
configure(router);
this.use(path, middleware, router);
return router;
}
router.prefix('/user', authMiddleware, async function (user) {
user.route('/details').get(function(req, res) {
res.status(201).send('Hello this is my personal details')
}); //also you can use controller method if you have any
});
module.exports = router //make sure you have to import/use this route in main/server js
You can use an npm module
Express group route
here is a code example from npm official module
var app = require('express');
require('express-group-routes');
app.group("/api/v1", (router) => {
router.get("/login", loginController.store); // /api/v1/login
});
Create the group method
export const group = ((callback: (router: Router) => void) => {
const router = express.Router();
callback(router);
return router;
});
Use the group method
import { group } from './relative/path/to/group/method'
const apiRouter = express.Router();
apiRouter.use('/foo', group((router) => {
router.get('/bar', (req, res) => {
res.send('Hello World');
});
}));
This will create a new GET route to "/foo/bar"
I just wrote this module to solve your problem: https://github.com/benjamin658/express-inject-middleware
You can group your middlewares as an array and pass it to the express-inject-middleware...
For example:
import express from 'express';
import { injectMiddleware } from 'express-inject-middleware';
const app = express();
const authMiddleware = (req, res, next) => {
// some auth logic...
};
const fooMiddleware = (req, res, next) => {
// some foo logic
}
const barMiddleware = (req, res, next) => {
// some bar logic
}
app.use(injectMiddleware(
[
authMiddleware,
fooMiddleware,
],
[
// Passing the app.[METHOD] as the parameter.
app.get('/secrets', (req, res, next) => res.send('secrets'));
// Mount barMiddleware itself
app.post('/secrets', barMiddleware, (req, res, next) => res.send('ok'));
],
));
and this is the result:
app.get('/secrets', authMiddleware, fooMiddleware, (req, res, next) => res.send('secrets'));
app.post('/secrets', authMiddleware, fooMiddleware, barMiddleware, (req, res, next) => res.send('ok'));
in express 4 to grouping your routes, you should create some changes :
seperate route files in multiple files like admin and front
pass the router to the divided route files in routes/index.js file
const express = require('express')
require('express-group-routes');
const router = express.Router()
require('./admin/web.router')(router)
require('./front/web.router')(router)
module.exports = router
in each files we can use groups, middlewares and prefixes, like below
const adminPrefix = 'admin'
module.exports = function (router) {
router.use(`\${adminPrefix}`, [ adminMiddleware ]);
router.group(`/${adminPrefix}`, (router) => {
router.get('/', home.index)
});
}
I have a pretty basic RESTful API so far, and my Express app is configured like so:
app.configure(function () {
app.use(express.static(__dirname + '/public'));
app.use(express.logger('dev'));
app.use(express.bodyParser());
});
app.post('/api/vehicles', vehicles.addVehicle);
How/where can I add middleware that stops a request from reaching my app.post and app.get if the content type is not application/json?
The middleware should only stop a request with improper content-type to a url that begins with /api/.
If you're using Express 4.0 or higher, you can call request.is() on requests from your handlers to filter request content type. For example:
app.use('/api/', (req, res, next) => {
if (!req.is('application/json')) {
// Send error here
res.send(400);
} else {
// Do logic here
}
});
This mounts the middleware at /api/ (as a prefix) and checks the content type:
app.use('/api/', function(req, res, next) {
var contype = req.headers['content-type'];
if (!contype || contype.indexOf('application/json') !== 0)
return res.send(400);
next();
});
Get content type from the request by using this.
req.get('Content-Type')
Example :
app.post("/api/test", (req, res) => {
console.log("Request type :", req.get('Content-Type'));
//your code
})
As an alternative, you can use the express-ensure-ctype middleware:
const express = require('express');
const ensureCtype = require('express-ensure-ctype');
const ensureJson = ensureCtype('json');
const app = express();
app.post('/', ensureJson, function (req, res) {
res.json(req.body);
});
app.listen(3000);
Adding this express middleware will validate all PATCH, POST and PUT and ensure that they contain the application/json header:
app.use((req, res, next) => {
if (['PATCH', 'POST', 'PUT'].includes(req.method) && !req.is('application/json')) {
res.send(400);
} else {
next();
}
});
Limiting it to certain methods will prevent errors with other methods such as GET
For input validation a good module is express-validator. It provides the middlewares needed to do any kind of check. In your case something like:
const { check, validationResult } = require('express-validator')
app.use('/api/', [
check('content-type').equals('application/json')
], function(req, res, next) {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
next();
});