New to Express. Want to implement an MVC pattern in Express and substitute routes folder with controller folder. I found this code, which actually works, but I don't really understand what it does:
var fs = require('file-system');
fs.readdirSync('controllers').forEach(function (file) {
if(file.substr(-3) == '.js') {
const route = require('./controllers/' + file);
route.controller(app);
}
})
The readdirSync reads the content of the folder 'controllers' and for each file it founds it does something that I don't understand:
if(file.substr(-3) == '.js') //checks if the end of the file is .js but why?
const route = require('./controllers/' + file); //don't understand this
route.controller(app); //don't understand this
Could you please help with this?
Thank you.
The example you follow comes from this blog entry by Tim Roberts. The example controller demonstrates what is it all about:
var mongoose = require('mongoose')
var Video = require('../models/user');
module.exports.controller = function(app) {
app.get('/signup', function(req, res) {
// any logic goes here
res.render('users/signup')
});
app.get('/login', function(req, res) {
// any logic goes here
res.render('users/login')
});
}
If you save this example controller in the controllers folder under whatever.js, all it does it exports a function, controller, that takes the express app as an argument and adds a couple of custom routes to the app.
Then, the main module scans all such files under the controllers folder and first uses the require function to load the module:
const route = require('./controllers/' + file);
After above line, the route contains a reference to the module that contains this controller function.
This
route.controller(app);
just calls the function exported from the module, passing the global app as an argument.
This way you can easily extend your app just by creating separate .js modules under controllers folder, that follow the same convention (export the controller function).
Related
Let Me explain what I am working on .
I have included the express module in app.js .
Here I want to access this module in index.js , admin.js , gallery.js.
but I dont want to write the same code again in all over js file .
let me show you my scenario.
app.js has
var express = require('express');
index.js has
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
gallery.js has
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.render('gallery', { title: 'Gallery' });
});
module.exports = router;
Here var express = require('express'); is already present in app.js .
I want to reuse it from app.js instead of including in every file .
What I have tried so far
I made global in app.js , so that I can access it all over
global.express = require('express');
this code is working for me .
Is this the correct approach or any other way to do or including express module in all the js file is fine ?
You can export your files (not app.js assuming this is your entry point) as a function and pass your express module into those files
gallery.js
module.exports = function(app) {
/* GET users listing. */
app.get('/', function(req, res, next) {
res.render('gallery', { title: 'Gallery' });
});
}
app.js
var express = require('express')
var app = express();
var galleryRoute = require('./gallery');
// Routes
galleryRoute(app);
I have read some where to avoid using Global Variables because there are some cons of it here are few of them:
The first reason is that when you create a global variable, it exists
throughout the lifetime of the application. When a variable persists
through the lifetime of the app it means that it is there, in memory,
occupying resources while the app is running.
Second, traditionally using global variables can cause concurrency
issues. If multiple threads can access the same variable and there
are no access modifiers or failsafes in place, it can lead to some
serious issues of two threads attempting to access and use the same
variable. However, while this is the case in other languages, it is
not necessarily the case for Node.js as it is strictly a
single-threaded environment. While it is possible to cluster Node
processes, there is no native way to communicate between them.
The last reason I am going to talk about is that using globals can
cause implicit coupling between files or variables. Coupling is not a
good thing when it comes to writing great code. When writing code, we
want to make sure that it is as modular and reusable as possible,
while also making sure it is easy to use and understand. Coupling
pieces of your code together can lead to some major headaches down
the road when you are trying to debug why something isn't working.
As for your question you can export the express from app.js or index and use it everywhere.
It is discouraged to use additional global variables as application already has 'export' and 'require' global variables. (exports/require are not keywords, but global variables.)
If you need to export the 'app' for other files, you can do something like below.
in index.js:
var express_app = module.exports = express();
now index.js can be required to bring app into any file.
in any.js file:
var express_app = require('./index');
I have a sails app and the routes,static assets associated with the app are served from root and it works fine. I would like to add a express middleware so i could serve the routes and static assets in a specific path.
In order to serve the static assets i used the below inside the customMiddleware function in config/http.js,
app.use('/my/new/path', express.static(path.join(__dirname, '../client', 'dist')));
With the above in place i was able to load the static files both from the root as well as from the /my/new/path.
Now when it comes to route i'm not sure how to handle the sails route to load via express middleware using app.use, for example the home route defaults to '/' in my config/routes-client.js, instead of altering the route there i would like to use something like below which we normally use for a typical node/express app,
app.use('/my/new/path', routes); --> where routes is my server routes
Is there a way to add a express middleware to serve sails route on a specific path?
I'm not sure why you'd want to have a prefix added automatically to your custom routes rather than just adding the prefix directly to the routes in the config/routes.js (or config-routes-client.js) file, but here goes:
// config/http.js
middleware: {
reroute: function (req, res, next) {
// Set the prefix you want for routes.
var prefix = '/foo/bar';
// Create a regex that looks for the prefix in the requested URL.
var regex = new RegExp('^' + prefix + '(/|$)');
// If the prefix is found, replace it with /
if (req.url.match(regex)) {
req.url = req.url.replace(regex, '/');
}
// Continue processing the request.
return next();
}
}
Make sure you add reroute to the middleware.order array in config/http.js. Incidentally, this will take care of static files as well.
This is how I do it...
Inside http.js
var express = require('express');
module.exports.http = {
customMiddleware: function (app) {
app.use('/docs', express.static(path.join(sails.config.appPath, '/docs')));
},
... rest of the http.js code...
}
Of course in this example im serving docs folder from root of my app on route /docs... You addapt to your logic...
I am currently developping a website that is using a lot of routes.
At the beginning, all the routes were implemented in a same file...
To make the things clearer, I decided to create multiple files, in order to separate the routes... using the Router module.
For example, in my users.js file I have:
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
module.exports = router;
and in my main app.js file :
app.use('/users', require('./routes/user');
This works perfectly but the thing is that I would like my 'users' route
to access some variables that have been declared into the app.js file, before the app.use(...)
I know I can use require and module.exports, but some variables must be declared in my app.js, only one time, and I must access them from the routes I include.
You can pass them as a configuration object. Also change your route, to a function that returns a route, this way it can take parameters.
module.exports = function (options) {
return function (req, res, next) {
// do your thing
// you have access to options variable as well
}
}
And in your main JS file, require the file and call it, and pass all you need.
app.use('/users', require('./routes/user')({
option1: 'value1',
option2: 'value2'
}));
For things like database connection, your models and other third party libraries, you don't need to pass them as a configuration object. It is a good practice to move them to external files as well and require them in your routes' file. You need to decouple as much as modules you can.
Just keep that in mind that require will load every module once, the rest of the time, it just returns the reference to previously loaded module. Take a look at this question for more info.
For a more global access, you make the variables you want to share global.For example
//app.js
GLOBAL.config = {}
It's best to use a separate file for you application config. For example
//config.js
module.exports = {logging:true}
and make it a global variable in your app.js file as follow
//app.js
GLOBAL.config = require('./config');
Now it will be available in your routing definition. For example
//routes/users.js
router.get('/', function(req, res, next) {
if(config.logAccess){
var accessTime = new Date();
console.log('access at '+accessTime.toTimeString());
}
res.send('respond with a resource');
});
I'm having trouble with this script
// App.js ( shrink )
var controller = require('./controller');
app.get('/', controller.index);
app.get('/home', controller.home);
// /controller/index.js
var meta = {
title: 'index',
description: ''
}
exports.index = function(req,res){
res.render('index', {
meta: meta
});
}
// /controller/home.js
var meta = {
title: 'glibet',
description: ''
}
exports.home = function(req,res){
res.render('home', {
meta: meta
});
}
Its returning me this Error: "Error: Route.get() requires callback functions but got a [object Undefined]"
Strangely enough it works just fine if i let app.get('/', controller.index); alone without the home route
I've tried a couple of corrections/alternatives in the code maintaining the system its way of invoking controller/files but it doesn't seem to fix the code, i will really appreciate any help.
PS: I'm trying to avoid setting a variable to each controller file, avoiding something like this code;
var homeController = require('./controllers/home');
var userController = require('./controllers/user');
app.get('/', homeController.index);
app.get('/login', userController.getLogin);
When you require ./controller, Node first looks for a file called controller. Failing that, it looks for a file called index.js inside the directory controller.
You are not requiring every file in a directory by using require() in this manner.
You could use a plugin such as require-dir for this purpose. However, my recommendation would be to require each file separately, so that you know exactly what you are making available to your page-- this prevents side effects where you accidentally leave a file in your routes directory that you didn't want required by your main application controller.
You are trying to require a directory of js files as an object. If you want to keep all of your controller files separate but have them represented in one object in your app, you could do something like this:
// /controller/controllers.js
var home = require('./home');
var index = require('./index');
module.exports = {
home: home.home,
index: index.index
}
By requiring './controller/controllers' you could keep all of your controller endpoints organized in different files while accessible in one object.
I'm just experimenting with basic routing in ExpressJS and so far I have two routes:
app.get('/', function(req,res) {
res.render('index');
});
app.get('/pics', function(req,res) {
res.render('pics');
});
When defined like this, in my app.js, everything works fine, but when exported as below, from individual files in my routes subdirectory, I get the error message that a callback function was expected, but an Object undefined was returned.
index.js:
exports.index = function(req,res) {
res.render('index');
});
pics.js
exports.pics = function(req, res) {
res.render('pics');
};
app.js
var routes = require('./routes');
app.get('/', routes.index);
app.get('/pics', routes.pics);
What am I doing wrong in the latter example to break everything?
The index route is working but your pics route isn't because you are trying to import it from index.js.
The route directory has index.js which means that if you do require('./route') you are actually doing require('./route/index'). This happens because index.js has a special meaning in Node.js.
So to get pics working you need to do:
app.get('/pics', require('./routes/pics').pics);
This can be really confusing and is a question that gets asked a lot on the IRC channel.
require('./routes') only loads ./routes/index.js, not ./routes/pics.js. So routes.pics will be undefined.
require() will try to load index.js.
Here is a small coffeescript snippet that you can paste (convert it to js) in index.js. It will autoload all your files(routes) in the current directory:
index.coffee
module.exports = (app) =>
require("fs").readdirSync(__dirname).forEach (file) ->
i = file.lastIndexOf('.')
ext = if (i < 0) then '' else file.substr(i)
if (ext == ".js" or ext == ".coffee") and file != "index"+ext
require("./" + file)(app)
app.js
require("./routes")(app)
someRoutes.js
app.get('/', function(req,res) {
res.render('index');
});