Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading Node.js modules dynamically based on route

People also ask

How do I import a module dynamically?

To load dynamically a module call import(path) as a function with an argument indicating the specifier (aka path) to a module. const module = await import(path) returns a promise that resolves to an object containing the components of the imported module. } = await import(path);

What is dynamic import NodeJS?

The dynamic import() loads the module and return a module object that contains all its exports. In order to access the default export use the default property of the module object: const moduleA = await import('./moduleA'); moduleA.

Which directive is used to load modules in node?

Require directive is used to load a Node module(http) and store returned its instance(http) into its variable(http).

CAN node run ES2020?

To provide backwards compatibility, Node. js allows you to import CommonJS packages with a default import. This flag tells TypeScript that it's okay to use import on CommonJS modules. This tells TypeScript that it's okay to output JavaScript syntax with features from ES2020.


If you know where your scripts are, i.e. you have an initial directory, for example DIR, then you can work with fs, for example:

server.js

var fs = require('fs');
var path_module = require('path');
var module_holder = {};

function LoadModules(path) {
    fs.lstat(path, function(err, stat) {
        if (stat.isDirectory()) {
            // we have a directory: do a tree walk
            fs.readdir(path, function(err, files) {
                var f, l = files.length;
                for (var i = 0; i < l; i++) {
                    f = path_module.join(path, files[i]);
                    LoadModules(f);
                }
            });
        } else {
            // we have a file: load it
            require(path)(module_holder);
        }
    });
}
var DIR = path_module.join(__dirname, 'lib', 'api');
LoadModules(DIR);

exports.module_holder = module_holder;
// the usual server stuff goes here

Now your scripts need to follow the following structure (because of the require(path)(module_holder) line), for example:

user_getDetails.js

function handler(req, res) {
    console.log('Entered my cool script!');
}

module.exports = function(module_holder) {
    // the key in this dictionary can be whatever you want
    // just make sure it won't override other modules
    module_holder['user_getDetails'] = handler;
};

and now, when handling a request, you do:

// request is supposed to fire user_getDetails script
module_holder['user_getDetails'](req, res);

This should load all your modules to module_holder variable. I didn't test it, but it should work (except for the error handling!!!). You may want to alter this function (for example make module_holder a tree, not a one level dictionary) but I think you'll grasp the idea.

This function should load once per server start (if you need to fire it more often, then you are probably dealing with dynamic server-side scripting and this is a baaaaaad idea, imho). The only thing you need now is to export module_holder object so that every view handler can use it.


app.js

var c_file = 'html.js';

var controller = require(c_file);
var method = 'index';

if(typeof controller[method] === 'function')
    controller[method]();

html.js

module.exports =
{
    index: function()
    {
        console.log('index method');
    },
    close: function()
    {
        console.log('close method');    
    }
};

dynamizing this code a little bit you can do magic things :D


Here is an example of a REST API web service that dynamically loads the handler js file based on the url sent to the server:

server.js

var http = require("http");
var url = require("url");

function start(port, route) {
   function onRequest(request, response) {
       var pathname = url.parse(request.url).pathname;
       console.log("Server:OnRequest() Request for " + pathname + " received.");
       route(pathname, request, response);
   }

   http.createServer(onRequest).listen(port);
   console.log("Server:Start() Server has started.");
}

exports.start = start;

router.js

function route(pathname, req, res) {
    console.log("router:route() About to route a request for " + pathname);

    try {
        //dynamically load the js file base on the url path
        var handler = require("." + pathname);

        console.log("router:route() selected handler: " + handler);

        //make sure we got a correct instantiation of the module
        if (typeof handler["post"] === 'function') {
            //route to the right method in the module based on the HTTP action
            if(req.method.toLowerCase() == 'get') {
                handler["get"](req, res);
            } else if (req.method.toLowerCase() == 'post') {
                handler["post"](req, res);
            } else if (req.method.toLowerCase() == 'put') {
                handler["put"](req, res);
            } else if (req.method.toLowerCase() == 'delete') {
                handler["delete"](req, res);
            }

            console.log("router:route() routed successfully");
            return;
        } 
    } catch(err) {
        console.log("router:route() exception instantiating handler: " + err);
    }

    console.log("router:route() No request handler found for " + pathname);
    res.writeHead(404, {"Content-Type": "text/plain"});
    res.write("404 Not found");
    res.end();

}

exports.route = route;

index.js

var server = require("./server");
var router = require("./router");

server.start(8080, router.route);

handlers in my case are in a subfolder /TrainerCentral, so the mapping works like this:

localhost:8080/TrainerCentral/Recipe will map to js file /TrainerCentral/Recipe.js localhost:8080/TrainerCentral/Workout will map to js file /TrainerCentral/Workout.js

here is a example handler that can handle each of the 4 main HTTP actions for retrieving, inserting, updating, and deleting data.

/TrainerCentral/Workout.js

function respond(res, code, text) {
    res.writeHead(code, { "Content-Type": "text/plain" });
    res.write(text);
    res.end();
}

module.exports = {
   get: function(req, res) {
       console.log("Workout:get() starting");

       respond(res, 200, "{ 'id': '123945', 'name': 'Upright Rows', 'weight':'125lbs' }");
   },
   post: function(request, res) {
       console.log("Workout:post() starting");

       respond(res, 200, "inserted ok");
   },
   put: function(request, res) {
       console.log("Workout:put() starting");

       respond(res, 200, "updated ok");
   },
   delete: function(request, res) {
       console.log("Workout:delete() starting");

       respond(res, 200, "deleted ok");
   }
};

start the server from command line with "node index.js"

Have fun!