How to require multiple controllers with express? - javascript

So I try to make a full stack project. It has controllers like signup, login, profile etc.
What I do is I require each controller manually, then use them according to the request. For example, I will have
app.use('/signup',signup);
app.use('/login',login);
app.use('/profile',profile);
However, when I build up the complexity of the project,more controllers will be needed, but manually typing everything is not the best practice.
I want to have a more general form, just one line does all the work:
app.use('whatever the link i got',load the corresponding controller);
Maybe the solution will be in different form. How can I achieve this? I need some suggestions.

The simplest solution would probably be to put them all in a controllers directory and then just do this:
const controllers = readdirSync(path.join(__dirname, 'controllers'))
controllers.forEach(controller => {
app.use(`/${controller}`, require(`./controllers/${controller}`))
})
This works great, as long as your routes and controllers are named the same thing. If you need to deal with converting kebab to camel case, there's always lodash.
Assuming you're using express 4, you could take this one step further, and put an index.js in the controllers directory. Inside:
const express = require('express')
const router = express.Router()
const controllers = readdirSync(__dirname))
.filter(f => f !== 'index.js'))
controllers.forEach(controller => {
router.use(`/${controller}`, require(`./${controller}`))
})
module.exports = router
Then you can just use the router like this:
app.use('/', require('./controllers'))

Related

Best Practice for Route Architecture

I'm trying to set up routes for my backend. I have two ways that I've tried setting these routes up, and I'm wondering which way fits best practices (or neither?). The differences are minimal, but I'd love to know if there is an objective "best" here.
Here are my attempts:
const express = require("express");
const router = express.Router();
const flashcardController = require('../controllers/flashcardController');
router.get('/', flashcardController.readFlashcard);
router.post('/', flashcardController.createFlashcard);
router.patch('/', flashcardController.updateFlashcard);
router.delete('/', flashcardController.deleteFlashcard);
module.exports = router
VS
const express = require("express");
const router = express.Router();
const flashcardController = require('../controllers/flashcardController');
module.exports = (app) => {
router.get('/api/flashcard', flashcardController.readFlashcard);
router.post('/api/flashcard', flashcardController.createFlashcard);
router.patch('/api/flashcard', flashcardController.updateFlashcard);
router.delete('/api/flashcard', flashcardController.deleteFlashcard);
app.use('/', router);
};
Of course, my app.js (entry-point for my backend) file will need to be coded slightly differently for each of these options.
If you believe that the job of a router is to just handle some requests that it receives and it is the job of the calling code to place the router at whatever path the calling code wants it to operate on, then only your first option will do that. This would allow a caller to use these routes in whatever path it wants.
If you want the module that implements the routes to be entirely self-sufficient and install the routes on the path it wants them to be on, then only the second option does that.
I would say that the "usual" and more "flexible" scheme is the first one where the caller places the routes on the path where it wants them. But, you are free to choose whichever style you want.
The second option is not implemented particularly efficiently so it could be improved. No router is needed at all as the routes can be just installed directly on the app object directly. And, repeating /api/flashcard multiple times can be avoided.
For example, the second option could be this:
const controller = require('../controllers/flashcardController');
const routePath = '/api/flashcard';
module.exports = (app) => {
app.get(routePath, controller.readFlashcard);
app.post(routePath, controller.createFlashcard);
app.patch(routePath, controller.updateFlashcard);
app.delete(routePath, controller.deleteFlashcard);
};
Or, even just this:
const controller = require('../controllers/flashcardController');
module.exports = (app) => {
app.route('/api/flashcard')
.get(controller.readFlashcard)
.post(controller.createFlashcard)
.patch(controller.updateFlashcard)
.delete(controller.deleteFlashcard);
};
And, the first one could be simplified to this:
const router = require("express").Router();
const controller = require('../controllers/flashcardController');
router.route('/')
.get(controller.readFlashcard)
.post(controller.createFlashcard)
.patch(controller.updateFlashcard)
.delete(controller.deleteFlashcard);
module.exports = router

Express.js routing. How to put a default route before all of my existing routes

I'm probably phrasing this question wrong so please bear with me. So right now I have some routes:
localhost:3000/index
localhost:3000/home
localhost:3000/login
localhost:3000/forgot
but before every route in the URL, I want a client name like this:
localhost:3000/client/index
localhost:3000/client/home
localhost:3000/client/login
localhost:3000/client/forgot
Is there a good way to do this without manually changing all the route strings? And again sorry if I'm phrasing this question poorly.
Create a router (router.js) with all those rules defined in it. Then reference in express app:
app.use('/client', require ('./router'));
You router file:
var router = require ('express').Router();
router.use('/index', ...);
...
module.exports = router;

What is the best way to pass global config variables to handlebars template?

Ive a nodejs app with express-handlebars and i am wanting to define variable for things like the 'host' address for CSS and Javascript that are currently being imported in a header.hbs file that i call form within the specific layout.
Ive created a config.js file which has a number of variables i want to set and ive imported that into the app.js using:
var config = require('./config.js');
but then im lost as t where to go. for example i was thinkging if i can some how do something like this:
<link href="{{config.csshost}}basev1.css" rel="stylesheet" type="text/css" />
Can anyone provide some pointers, am stumped other than declaring these variable every time i load the template.
You would set the app locals:
var app = express()
app.locals = {
config: config,
templateVar: 'test'
}
Edit:
Your routes will look something like this:
app.get('/', function(req, res) {
res.render('index', {config: config});
})
What this does, is then update the app.locals variable in express to look like this:
app.locals = {
config: config
}
All the app.local variables are then accessible in your templates via:
{{config}}
//which is really
app.locals['config']
So, in your app.js where you configure express you would do this:
var app = express();
app.locals.config = require('./config')
app.get('/', function(req, res) {
return res.render('index')
})
(This should be a comment to sctskw's answer above, but I don't have enough reputation to do that.)
Rather than overwriting app.locals with a new object, it would be better to add new properties to the existing one, like this:
app.locals.config = config;
app.locals.templateVar = 'test';
The reason is that Express uses app.locals internally. Overwriting it would lose some useful variables Express provides. For example, we could use settings.env to detect the current running environment in a view template, which is defined as app.locals.settings.env by Express.

Play framework Javascript router not working for controller in different directory

This question is concerned with the less verbose way of giving Play's javascript router
Less Verbose way of generating Play 2's javascript router
I have two route mappings in my routes file:
GET /attachments/:id/content com.application.controllers.File.getOrderContent(id:String)
POST /drive com.application.controllers.myapp.Drive.createDrive()
I have the non-verbose Javascript router code in a scala file:
val routeCache = {
import routes._
val jsRoutesClass = classOf[routes.javascript]
val controllers = jsRoutesClass.getFields().map(_.get(null))
controllers.flatMap { controller =>
controller.getClass().getDeclaredMethods().map { action =>
action.invoke(controller).asInstanceOf[play.core.Router.JavascriptReverseRoute]
}
}
}
def javascriptRoutes = Action { implicit request =>
Ok(Routes.javascriptRouter("jsRoutes")(routeCache:_*)).as("text/javascript")
}
I am getting an "Uncaught TypeError: Cannot read property 'Drive' of undefined" when I am using
jsRoutes.com.application.controllers.myapp.Drive.createDrive().ajax({...
whereas
jsRoutes.com.application.controllers.File.getOrderContent().ajax({... works fine.
Any thoughts on why the com.application.controllers.myapp.Drive path is not recognized by the router? I thought all the paths in the routes directory would be included in jsRoutes.
There is one one routes object and one javascript generated per package. So in your case you will have controllers.(routes|javascript) containing all the controller routes for the controllers in the package controllers and a separate controllers.myapp.(routes|javascript) for those in controllers.myapp
You can find the generated sources under target/scala-[version]/src_managed/main/controllers/routes.java and target/scala-[version]/src_managed/main/controllers/myapp/routes.java.

node.js variable scope with routes separation

my routes are defined in an external folder
./routes
here's the way i define the routes in my server.js file
app.get('/', routes.index);
app.post('/validation', register.valid);
the register.valid module, which is originally written in
./routes/validation.js
is responsible for creating a new user account and register it into a database (MongoDB).
How can i access an object from server.js in validation.js ? First, i thought declaring the object before defining my routes would resolve the case, but actually it doesn't seem to be the solution.
I'm assuming your current code already does work (that is, you receive the posted data), but you need access to another object from validation.js.
If your code works, then you probably have this line in server.js:
var register = require('./routes/validation');
And you need acess to the variable obj in the validation module. You could have a function inside the validation module:
var foo;
exports.configure = function(obj) {
foo = obj;
}
The exports mean the variable configure will be accessible to modules which "require" the validation module. This way you can do, inside the server.js module:
register.configure(obj);
app.post('/validation', register.valid);
The exact configuration of this will depend on what you are actually trying to accomplish. Sometimes, for example, it's good to have a database object stored in a global variable.
Generally in this kind of structure server.js will create the app object and then pass that to individual routes modules via a function. I do it by having each router module export a single function like this:
//routes/validation.js
function setup(app) {
app.get(....blah
app.post(....blah
}
module.exports = setup;
Then I tie that together in server.js like this:
//server.js
var express = require('express');
var app = express();
require('./routes/validation')(app);
See also my express_code_structure sample project for other code organization tips.

Categories