How can I share code between Node.js and the browser?

Viewed 86909

I am creating a small application with a JavaScript client (run in the browser) and a Node.js server, communicating using WebSocket.

I would like to share code between the client and the server. I have only just started with Node.js and my knowledge of modern JavaScript is a little rusty, to say the least. So I am still getting my head around the CommonJS require() function. If I am creating my packages by using the 'export' object, then I cannot see how I could use the same JavaScript files in the browser.

I want to create a set of methods and classes that are used on both ends to facilitate encoding and decoding messages, and other mirrored tasks. However, the Node.js/CommonJS packaging systems seems to preclude me from creating JavaScript files that can be used on both sides.

I also tried using JS.Class to get a tighter OO model, but I gave up because I couldn't figure out how to get the provided JavaScript files to work with require(). Is there something am I missing here?

15 Answers

If you use use module bundlers such as webpack to bundle JavaScript files for usage in a browser, you can simply reuse your Node.js module for the frontend running in a browser. In other words, your Node.js module can be shared between Node.js and the browser.

For example, you have the following code sum.js:

Normal Node.js module: sum.js

const sum = (a, b) => {
    return a + b
}

module.exports = sum

Use the module in Node.js

const sum = require('path-to-sum.js')
console.log('Sum of 2 and 5: ', sum(2, 5)) // Sum of 2 and 5:  7

Reuse it in the frontend

import sum from 'path-to-sum.js'
console.log('Sum of 2 and 5: ', sum(2, 5)) // Sum of 2 and 5:  7

Use case: share your app configuration between Node.js and the browser (this is just an illustration, probably not the best approach depending on your app).

Problem: you cannot use window (does not exist in Node.js) nor global (does not exist in the browser).

Edit: now we can thx to globalThis and Node.js >= 12.

Obsolete solution:

  • File config.js:

      var config = {
        foo: 'bar'
      };
      if (typeof module === 'object') module.exports = config;
    
  • In the browser (index.html):

      <script src="config.js"></script>
      <script src="myApp.js"></script>
    

    You can now open the dev tools and access the global variable config

  • In Node.js (app.js):

      const config = require('./config');
      console.log(config.foo); // Prints 'bar'
    
  • With Babel or TypeScript:

      import config from './config';
      console.log(config.foo); // Prints 'bar'
    

I wrote a simple module, that can be imported (either using require in Node, or script tags in the browser), that you can use to load modules both from the client and from the server.

Example usage

1. Defining the module

Place the following in a file log2.js, inside your static web files folder:

let exports = {};

exports.log2 = function(x) {
    if ( (typeof stdlib) !== 'undefined' )
        return stdlib.math.log(x) / stdlib.math.log(2);

    return Math.log(x) / Math.log(2);
};

return exports;

Simple as that!

2. Using the module

Since it is a bilateral module loader, we can load it from both sides (client and server). Therefore, you can do the following, but you don't need to do both at once (let alone in a particular order):

  • In Node

In Node, it's simple:

var loader = require('./mloader.js');
loader.setRoot('./web');

var logModule = loader.importModuleSync('log2.js');
console.log(logModule.log2(4));

This should return 2.

If your file isn't in Node's current directory, make sure to call loader.setRoot with the path to your static web files folder (or wherever your module is).

  • In the browser:

First, define the web page:

<html>
    <header>
        <meta charset="utf-8" />
        <title>Module Loader Availability Test</title>

        <script src="mloader.js"></script>
    </header>

    <body>
        <h1>Result</h1>
        <p id="result"><span style="color: #000088">Testing...</span></p>

        <script>
            let mod = loader.importModuleSync('./log2.js', 'log2');

            if ( mod.log2(8) === 3 && loader.importModuleSync('./log2.js', 'log2') === mod )
                document.getElementById('result').innerHTML = "Your browser supports bilateral modules!";

            else
                document.getElementById('result').innerHTML = "Your browser doesn't support bilateral modules.";
        </script>
    </body>
</html>

Make sure you don't open the file directly in your browser; since it uses AJAX, I suggest you take a look at Python 3's http.server module (or whatever your superfast, command line, folder web server deployment solution is) instead.

If everything goes well, this will appear:

enter image description here

Related