Communication between middleware and route in keystonejs - node.js

I am using keystone and I have productDetail route in which I can add variables in res.locals to be used in templates. Is there a way I can use res.locals (of route file) in middleware.js file? As right now middleware is executing before route, I want route file to be executed first.
This is where middleware is executing in index.js file
keystone.pre('routes', middleware.initLocals);
And after that we have
exports = module.exports = function(app) {
// Views
app.get('/', routes.views.index);
app.get('/product-detail/:product', routes.views.productDetails);
}

I'm not sure if I got your question but this might help. You can run as many custom middleware you want after the middleware.initLocals (which apparently runs first). In your routes/middleware.js file, you can have, for example, two middleware:
exports.middleware0 = function (req, res, next) {
// Do some stuff
next();
};
exports.middleware1 = function (req, res, next) {
// Do some other stuff
next();
};
Then, inside your routes/index.js you can chain middleware together:
//...
var middleware = require('./middleware');
//...
exports = module.exports = function (app) {
// Use the middleware0 and middleware1:
app.get('/product-detail/:product', [middleware.middleware0, middleware.middleware1], routes.views.productDetails);
};

Related

Express: Add middleware on static folder

Hello I have express app where i need to plug a custom middleware/logic before accessing static folder for serving files, how i can achieve that without applying this middleware on every route. For now code looks like:
function middleware() {
console.log('hello');
}
app.use(middleware).use(express.static('public'));
app.listen(8000, () => {
console.log('server running on 8000');
});
app.get('/hi', (req, res) => {});
Issue is when hi is called middleware is executed as well and I want to execute it only if static files from public folder are called
first of all create a middleware.js in the middleware you can impalement business logic
module.exports = (req, res, next) => {
let substring = ".html"; // static files
if (req.originalUrl.includes(substring)) {
console.log("hello");
}
next();
};
in app.js
const middleware = require('./middleware');
app.use(middleware).use(express.static('public'));
app.listen(8000, () => {
console.log('server running on 8000');
});
app.get('/hi', (req, res) => {});
I see 2 options:
either your middleware first checks that the requested file exists
app.get('*', myMiddleware, express.static('public'))
or you run your middleware and static middleware if the path LOOKS like the static
app.get('*(png|jpg|css)', myMiddleware, express.static('public'))
In both cases you can chain middlewares together (note multiple params in app.get). So that they are executed in this order and you can control the chain by using next() and next('route')
Your middleware would look like this:
function myMiddleware (req, res, next) {
// optionally check if file exists depending if you take option 1
if (/* file exist check based on req.path */) {
// if file doesn't exit, go to next router
next('route') // here 'route' is a magic word, see https://expressjs.com/en/guide/using-middleware.html
}
console.log('hi')
next() // don't forget this!
}
NOTE: next('route') in option 1 only works with app.get not simple app.use, but i hope you only need GET for your static

How to split a routes.js that grows too large to be easily maintained?

I'm using node and express to create a rest api. I followed a tutorial where all the routes and its logic are saved in a routes.js file like this:
SERVER JS:
var express = require('express');
var app = express();
(...)
require('./app/routes.js')(app, port, express);
ROUTES.JS
module.exports = function(app, port, express) {
var apiRoutes = express.Router();
(...)
//Sample route
apiRoutes.get('/userfiles', function(req, res) {
UserFile.find({ owner: req.decoded.user.email }, function(err, filesList) {
if (err)
return done(err);
res.json({ success: true, files: filesList });
});
});
My problem is twofold:
1 - Routes can easily contain code thats 150 lines long, some of them far longer. It doesn't feel clean to have route declarations and the logic grouped together. Is it a good practice to do something like this instead?
apiRoutes.post('/randomRoute', function(req, res) {
return res.json(functionThatContainsTheActualCode(req));
});
(and then have an functionThatContainsTheActualCode function with all the logic in a different file).
2 - I have middleware that applies to some functions (for example, some routes are only accessible for logged in users and those routes go through an authentication middleware). Currently way I do it is declaring public routes before the middleware declaration and private routes after, which feels incredibly hacky. How can I separate public and private routes (and the middleware itself) in different files?
Problem 1:
We need to go deeper.
Change the route file to just require the actual router logic.
routes.js
// where app = express();
module.exports = (app) => {
// index.js happens to be a file exporting the router.
app.use('/', require('./index'));
// this is basically the idea. Create a separate file for the actual logic.
app.use('/route', require('.path/to/file'));
};
and in file.js
const express = require('express'),
router = express.Router();
router.verb('/path/', (req, res, next) => {
// do whatever
});
// this is required
module.exports = router;
Problem 2:
Middleware is basically a function taking in request, response, next as 3 params, doing something with the request and either sending out a response or moving on to the next middleware. That's why you need to call next if you want to move to next middleware in the chain.
Now all you need is a file that exports a function which takes request, response, next as params.
// lets call this auth.js
module.exports = function(req, res, next) {
// do logic
if () {
return res.send(); // or res.somethingThatSendsOutAHttpResponse()
}
// next middelware
next();
};
Since express routes are also middlewares, (mind blown), you can mount them top down.
To authenticate a route, just put the auth.js middleware on top of that route.
router.get('/', require('./auth'));
router.get('/', require('./log'));
router.get('/', (req, res, next) => {
// yolo
});
Now since this is web dev, you still got problems.
Now all your boring database queries are scattered everywhere.
Fear not, you can solve it, by, guess, creating another file.
apiRoutes.get('/userfiles', function(req, res) {
const userFile = require('/path/to/model/with/userfile/methods/exported/out');
// do something with userFile's methods
});

Easiest way to pass variables to routes templates in Express?

I've just made an Node.js app modular by splitting up data models and routes into separate files.
My routes are exported by express.Router(). In these routes I would like to import queried values from my app.js to be rendered with the templates.
How would I in the easiest way save things lets say with app.locals or req.variableName?
Since the route using express.Router() ties it together with app.js, should I be using app.params() and somehow make these values accessible?
Using globals seems like a worse idea as I'm scaling up the app. I'm not sure if best practice would be saving values to the process environment either using app.locals.valueKey = key.someValue...
Big thanks in advance to anyone
If I understand the question correctly, you want to pass a value to a later middleware:
app.js:
// Let's say it's like this in this example
var express = require('express');
var app = express();
app.use(function (req, res, next) {
var user = User.findOne({ email: 'someValue' }, function (err, user) {
// Returning a document with the keys I'm interested in
req.user = { key1: value1, key2: value2... }; // add the user to the request object
next(); // tell express to execute the next middleware
});
});
// Here I include the route
require('./routes/public.js')(app); // I would recommend passing in the app object
/routes/public.js:
module.export = function(app) {
app.get('/', function(req, res) {
// Serving Home Page (where I want to pass in the values)
router.get('/', function (req, res) {
// Passing in the values for Swig to render
var user = req.user; // this is the object you set in the earlier middleware (in app.js)
res.render('index.html', { pagename: user.key2, ... });
});
});
});

Move routes into files in Express.js

Say I have some routes (I have a lot more, but this should explain):
router.post('/post');
router.get('/post/:id');
router.get('/posts/:page?');
router.get('/search');
For the /post ones I know I could do something like
app.use('/post', postRoutes)
Where postRoutes is the actual post routes in another file. However, I'd like to group all post related routes into a postRoutes component (so /post and /posts), search into a search component and so on. Is there a way to do something like
router.use(postRoutes); // includes routes 1-3 above
router.use(searchRoutes); // only the 4th route above
And so on? That would let me keep the top level file much cleaner.
Yes it is simple. You can even make more nesting levels. I think it is good to separate routes, especially when you have dozens of routes.
in your first file (server.js)
app.use(require("./allpost"));
app.use(require("./allqueries"));
in allpost.js
var express = require('express');
var router = new express.Router();
router.post('/post', function (req, res) {
//your code
});
router.get('/post/:id', function (req, res) {
//your code
});
router.get('/posts/:page?', function (req, res) {
//your code
});
when you want more nesting
router.use(require("./deeper"));
or when you want use path part
router.use("/post2/", require("./messages/private"));
module.exports = router;
You could do that by creating a special route file. Here's an example of such file
module.exports = (function() {
var express = require('express');
var router = express.Router();
router.get("/:id", function (request, response, next) {
request.body.id = request.params["id"];
// Do something ...
});
router.post("/someRoute", function (request, response, next) {
// Do something ...
});
// And so on ...
return router;
})();
Next, in you server.js file, include it like this
app.use('/post', require('./routes/postRoutes'));
The problem was I was thinking about this wrong. First off, don't use singular and plural. It makes it a headache and also makes it hard for people to remember the API.
Once I used all plural I had a setup like this in my index.js file:
// The API routes all start with /api and we pass app here so we can have some
// sub routes inside of api
app.use('/api', require('./app/api')(app));
And then in my api/index.js
var express = require('express');
var router = express.Router({ mergeParams: true });
var routeInit = function (app) {
app.use('sessions', require('./sessions')(router));
app.use('users', require('./users')(router));
return router;
};
module.exports = routeInit;
You can see that I'm passing the router manually each time. Then finally:
var routeInit = function (router) {
router.post('/blah', function (req, res, next) {
// Do stuff
});
return router;
};
module.exports = routeInit;
This allowed me to nest routes infinitely deep.

How to put middleware in it's own file in Node.js / Express.js

I am new to the whole Node.js thing, so I am still trying to get the hang of how things "connect".
I am trying to use the express-form validation. As per the docs you can do
app.post( '/user', // Route
form( // Form filter and validation middleware
filter("username").trim()
),
// Express request-handler gets filtered and validated data
function(req, res){
if (!req.form.isValid) {
// Handle errors
console.log(req.form.errors);
} else {
// Or, use filtered form data from the form object:
console.log("Username:", req.form.username);
}
}
);
In App.js. However if I put something like app.get('/user', user.index); I can put the controller code in a separate file. I would like to do the same with the validation middleware (or put the validation code in the controller) to make the App.js file easier to overview once I start adding more pages.
Is there a way to accomplish this?
Basically I would like to put something like app.get('/user', validation.user, user.index);
This is how you define your routes:
routes.js:
module.exports = function(app){
app.get("route1", function(req,res){...})
app.get("route2", function(req,res){...})
}
This is how you define your middlewares:
middlewares.js:
module.exports = {
formHandler: function(req, res, next){...}
}
app.js:
// Add your middlewares:
middlewares = require("middlewares");
app.use(middlewares.formHandler);
app.use(middlewares...);
// Initialize your routes:
require("routes")(app)
Another way would be to use your middleware per route:
routes.js:
middlewares = require("middlewares")
module.exports = function(app){
app.get("route1", middlewares.formHandler, function(req,res){...})
app.get("route2", function(req,res){...})
}
I hope I answer your questions.
You can put middleware functions into a separate module in the exact same way as you do for controller functions. It's just an exported function with the appropriate set of parameters.
So if you had a validation.js file, you could add your user validation method as:
exports.user = function (req, res, next) {
... // validate req and call next when done
};

Resources