0
\$\begingroup\$

I took some time to try and solve a problem. I am building a basic Node + Express API. In the app I have created a models folder and will be adding new models as I continue development.

I attempted to write a small autoloader for a directory to automatically load and export my models. This code snippet should work for any modules not just my models folders.

See the following code:

loader.js

const fs = require('fs');
// Autoload modules.
module.exports = fs.readdirSync(__dirname)
                    // Ignore current file (loader.js)
                    .filter(f => !__filename.includes(f))
                    // Require all files in directory
                    .map(f => require( `./${f}` ))
                    // Add to final module.exports object using class name
                    .reduce((prev, curr) => ({...prev, [curr.name]: curr}), {});

My folder structure for the models:

Structure of my models directory

How I import the models into my Routes file:

author.route.js

const { AppResponse, Author } = require('../models/loader');

The question:

Is this a good idea to do something like this? My reasoning is that it would minimize changes when I commit and not require any modifications to my code should I decide to add new functionality to my routes and models folders.

What caveats are there to doing this and would there be a better approach?

Thanks for taking the time to look at this!

\$\endgroup\$
  • 3
    \$\begingroup\$ I appreciate critisism but perhaps explain the downvote? I would gladly ammend my question should it not have sufficiant information. \$\endgroup\$ – Dewald Els Apr 10 at 16:48
  • 4
    \$\begingroup\$ You need to supply more of your code for it to be reviewable; small excerpts are not reviewable. \$\endgroup\$ – Reinderien Apr 10 at 19:13
  • \$\begingroup\$ @Reinderien This is clearly a Node.js design pattern in itself and not an “excerpt” \$\endgroup\$ – Christophe Marois Apr 11 at 6:32
  • \$\begingroup\$ @Reinderien I've added more detail. Hopefully it helps \$\endgroup\$ – Dewald Els Apr 11 at 12:35
2
\$\begingroup\$

tl;dr

The major reason for not doing this is that dynamically requiring modules breaks static analysis.

In a pure Node.js project, this does not have major repercussions aside from breaking IDE's autocompletion. Everything else will still work as expected.

diving deeper

Should this pattern be used in a webpack project for instance (targetting server or client), it would fail, as bundlers need to statically compute the dependency tree at compile-time.

The require() function (CommonJS) was implemented in Node because, at the time, the core Javascript language didn't have the concept of modules. However this has changed, as ES modules are now available in all evergreen browsers, and in Node 13.x without a feature flag.

The new keywords import and export do not support variable interpolation for the purpose of static analysis, and are even required to be declared on top of the module. In practice, this makes the patterns if (dev) { require('debug') } or require(name) unusable, once adapted to ES modules.

recommendation

In my opinion, this is a case where anything “clever” you try to make will just make the code harder to read and maintain. I've been down that path too, and I can tell you that the long-term costs are not worth the 2 seconds it will take you to add manual exports to your files. In my experience, reading code is 10 times more costly than writing it.

You also don't even have to create index.js loaders; you can require the files directly. Ryan Dahl, creator of Node, even said at JSConf 2018 that he considered the addition of index.js a mistake because “it needlessly complicated the module loading system.”

Good luck, and be kind to future-you.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks for this great answer. I have not considered the impact of bundlers at all! Clearly this approach would then not be "Future proof" when switching to ES modules. \$\endgroup\$ – Dewald Els Apr 11 at 9:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.