Reputation: 944
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!
Upvotes: 1
Views: 71
Reputation: 2093
The easiest way to explain the first code snippet would be to rewrite it and add appropriate comments:
//Get all file names inside the controllers directory
const fileList = readdirSync('app/controllers');
//Get only directories from the file list
const onlyDirectories = fileList.filter(name => lstatSync(`app/controllers/${name}`).isDirectory());
//Create an empty object that will be exported from this module. Properties of this object will be assigned in the "for of" loop
const objectToExport = {};
//Import all files from the given directory and assign them to objectToExport
for (let directoryName of onlyDirectories){
//First get all file names
const controllerFiles = readdirSync(`app/controllers/${directoryName}`);
//Then require each of these files
const controllerModules = controllerFiles.map(fileName => require(`./${directoryName}/${fileName}`));
//Assign the imported modules to `objectToExport`
controllerModules.forEach((controllerModule) => {
//Validate each module and assign it only if it passes validation
if (validationSchema(directoryName, controllerModule)){
objectToExport[`${directoryName}Controller`] = controllerModule;
}
});
}
module.exports = objectToExport;
Below I'll address your follow-up questions from the comments. The resulting object now looks like this:
{
userController: {
registration: [Function],
...
},
orderController: {
...
},
...
}
The registration function ended up in the userController
property because it was exported by registration.js
, which was then imported by the require statement in my first code snippet. In order to use this function directly in other files, you have to destructure it in the following way:
const { userController: {registration} } = require('../controllers/');
//now you can use it like this:
router.post('/registration', registration);
Your last question is about req.body
. As you can see, the function takes two parameters: req and res:
exports.registration = async (req, res) => await UserService.create(req.body);
It is then passed to your router as a middleware. I'm assuming you're using Express.js as your framework. If so, req
and res
are passed automatically by the router to the registration
function. That's how they can be used by UserService
inside this function. The body
property is created automatically by Express as described here.
Upvotes: 1