I am a junior NodeJS dev, currently working on a cryptocurrency exchange platform. here is the project folder structure.
/app
/controllers
/user
/order
/settings
...
index.js
/middlewares
/models
/routes
user.js
order.js
/services
user.js
order.js
...
/views
index.js
/config
/migrations
/public
/utils
server.js
.env
...
Now, at first, it was a bit overwhelming, but later on, I became comfortable moving around the app.
except for one particular file!
the index.js in the controllers directory,
here is how it is set up.
const { readdirSync, lstatSync } = require('fs');
const { validationSchema } = require('../../utils/validator');
module.exports = readdirSync('app/controllers')
.filter(name => lstatSync(`app/controllers/${name}`).isDirectory())
.reduce((controllersAccumulator, dir) => Object.assign(
controllersAccumulator,
{
[`${dir}Controller`]: readdirSync(`app/controllers/${dir}`)
.map(fileName => require(`./${dir}/${fileName}`))
.reduce((accum, controllerFile) => Object.assign(
accum,
Object.keys(controllerFile).reduce(validationSchema.bind(null, dir, controllerFile), {}),
), {}),
},
), {});
I have to admit, this has been always scary to me, just to look at it! so what it does in simple words is, it maps the routes requests to the handlers in the controllers directory.
so for example, If a user wants to make a post request to register a new account: the route path will be like so:
// in the routes dir, user.js
const { userCOntroller } = require('../controllers/');
router.post('/registration', userController.registration);
// in the /controllers, then /user, there will be a registration.js that includes:
const UserService = require('../../services/user');
exports.registration = async (req, res) => await UserService.create(req.body);
//then in the /services directory, user.js
...
class UserService {
static create(body) { ... }
...
}
module.exports = UserService
so what I am still unable to understand is, how did we come about to have the userController which is imported in the user routes in the last snippet? so this is what the index.js file in the /controllers has produced!
when I asked the senior guys in the team, they said, yes it's hard to read but its less code.
well, ok :\
so, what could have been done differently to make this file more readable, in other words, is there a way to refactor it? thanks in advance!
userControllercomes from[${dir}Controller]because${dir}is replaced with the directory name like 'user', 'order', 'settings' etc, so the resulting module properties are calleduserController,orderControllerand so on. The last code snippet uses destructuring to extract one of these properties, in this case, theuserController. That's where the name comes from. The actual controller is imported by the following statement:require(./${dir}/${fileName}). The required files are then filtered (reduced) by thevalidationSchemafunction.userControllersand then calluserController.registerationI want to importregistrationfunction directly in the user routes file, and where should I pass the req.body in this case? thanks again,,userController.registrationis just a file as far as I understand, I mean its not a function call, how is that ?? I mean how is this req.body passed in the particular situation ??registration.jsexports an object which contains the registration function and resides inside theuserdirectory. If that's the case, thenvalidationSchemashould return its second argument (controllerFile). That's how theregistrationfunction can appear inside theuserControllerobject. In order to use this function directly, instead of destructuring the whole user controller, you could do something like this:const { userController: {registration} } = require('../controllers/');. Then you could call theregistrationfunction directly.