How to Make Your Own Node Modules to Share Code Between the Client and the Server in Javascript

This is a tutorial for people wanting to share javascript code between the sever and the front end.

This is useful in cases where your site loads server side but then performs similar actions client side.

One way to do this using the Node/Express model/view/controller layout is to create your own local NPM modules, and that is what this tutorial teaches you.

The main steps are:

  1. Create a folder in the project that will be your new npm module. Call it something like “sharedFunctions”

  2. Navigate to the folder then run npm init to start a new package.json

  3. Name the package as you like

  4. add "private: true, to the package.json to keep from accidentally uploading your package

  5. Create an index.js in the new module file and define functions there like

exports.example = function(parameter)
{

var result=parameter * 2

return result;

}
  1. In your client side (or server side) javascript create an index.js file that will import the functions from your module. I.E.
const clientsideJS=require("package-name"); //The name you gave your npm package


myExampleOutput = ClientsideJS.example(2) //2 = parameter to pass to the function in the npm module
//Test the code
console.log(myExampleOutput(2));

  1. After that run npm install ../path/to/npm_package/ to install the package you just made! Not this is different from the usual npm install

  2. You can then install a bundler like parcel to bundle your module into a package that will be included client side

1 Like

Another Method

If you are using a bundler like parcel you don’t even have to make an npm module to share the files, you can just use requires.

So the steps would be:

  1. Create a folder for functions shared by the server and client:
    shareFunctionFolder

  2. Create an index.js in that folder and define functions in it
    I.E.

exports.addFood= function(numberIn)
{

var numOut=numberIn + 1;

return numOut;

}
  1. Then create a folder for bundled JS to be included client side

  2. Add an index.js to this folder then add code to import the functions. I.E.:

const tools=require("shareFunctionFolder");
const myFrontEndData=require("./test");




console.log(tools.addFood(myFrontEndData.clientData=tools.addFood(100);)

And that should be it.

Some questions to the forum.
Why not use gulp or webpack over parcel?

Parcel generates 5k of code to manage all this that gets included in the production bundle. Not terrible, but something to be aware of.

This seems to be a great article around bundlers and why to choose one over the other:

Basically parcel is the best at tree shaking and getting rid of un-needed JS. Webpack has the most options, but takes time to learn.

1 Like

Parcel is nice because it doesn’t need any configuration. You can create an index file and import JS files, TypeScript files, SCSS files, or many other languages, and it will build the output for you as compressed, browser-friendly JavaScript. If you don’t need anything more complicated than that, it’s great.

Here’s an example that uses SCSS (compiles to CSS) and TypeScript (compiles to JS), and then inlines the CSS and JS code in the compressed HTML output.

  • The left sidebar shows the file structure.
  • The middle pane has a TypeScript file (top), an SCSS file (middle), and an HTML file (bottom). The HTML file directly imports the .ts and .scss file without any more information. (Webpack would require a lot more configuration and setup.)
  • The right pane shows the build output. The CSS and JS have been inlined and everything is minified. (Parcel still added about 1.2 KB of weight to the JS.)

I uploaded the code here in case anyone wants to check it out.

To get the dependencies:

$ npm install

To build the output to a new directory named dist:

$ npm build

To run the project in development mode and serve it at localhost:1234:

$ npm start
1 Like

Hmm I like parcel pretty well, but I have a few more questions.

  1. Is there a way I can have the bundle file saved as something other than index.js? How is cache busting going to work on the clientside?

  2. Right now I am still figuring out function import and exports.

So I have a shared-functions folder that contains functions I want to share between front end and back end.

When I “export” this function to the client side JS bundle I do it by defining the function as we went over yesterday. So in the index.js file of the shared-functions folder I have

exports.addFood=function(numberIn)
{

var numOut=numberIn + 1;

return numOut;

}

Then in the index.js of my clientSideJS folder I have

const tools=require("../shared-functions");

This lets me import those functions from the index.js and I can call them using: tools.addFood

Now the question is, how can I have functions in other files in the shared-functions folder?

I tried making a new file called addFood.js and putting the function there.
Something like this:

const addFood=function(numberIn)
{

var numOut=numberIn + 1;

return numOut;

}

module.exports={addFood};

Then in index.js I put something like


const addFood=require('./addFood');


module.exports = {
    addFood
};

But parcel breaks at this point. Trying different variations doesn’t work. So I am wondering what is the way to have multiple files in the shared-functions folder so that parcel can find and bundle them?

1 Like

I just woke up, and this is untested, but you might be able to generate a random string when the server boots and then append it to the script’s URL. You could try to do it with a Handlebars helper like this (unless someone else here has a better idea).

EDIT: actually, what I originally posted might generate a new date on every page load, so it wouldn’t be ideal. I moved it out of the helper function, so I think this will only generate the time stamp once when the server starts, but it should be double-checked to make sure it isn’t changing on every page load.

// Create a new timestamp in hex format that would be generated
// every time you start the server. Example: "1723dc22d5b"
const currentTime = Date.now().toString(16);

// I don't remember what syntax you're using to define helpers, but something
// like this might work.
hbs.registerHelper('cacheBuster', function() {
    return currentTime;
});

Then something like this in your layout.hbs:

<script src="/js/build/bundle.js?c={{cacheBuster}}"></script>

If the cacheBuster string is generated when the server is started, then it would be unique each time you deploy the app.

Here’s a quick example:

The important files:

.
├── app.js
└── tools
    ├── conversions.js
    ├── data.js
    └── index.js

The program is just a random example. The app.js file could be any file that uses the tools package. Inside the tools directory are various modules (files) that perform various types of functions. tools/conversions.js has functions for conversions, and tools/data.js has some fixed data related to the tools. The tools/index.js file imports the other files in that directory and exports them in a single object.

You can then do:

// import all the functionality from the directory
const tools = require("./tools");

// convert Fahrenheit to Celsius
tools.f2c(100);

// find out what scale is used in Spain
tools.countries["Spain"];

I left some notes in the code. Let me know if you want me to walk through it in more detail.

1 Like

By the way, there is some ES6 in those files I posted in my last comment, so some of that syntax might not work in older browsers if you don’t transpile it. Parcel will transpile it automatically, so you don’t have to worry about that kind of syntax if using Parcel.

Parcel transforms the JS into code that can run in all browsers that have more than 0.25% marketshare. If you want more control over what browsers the JS output supports, there are additional configuration options.

1 Like

This is great. Thanks Josh!

1 Like

So I just want to note, one stumbling block for me was not realizing when you do module.exports you are either exporting a function or an object. When you include the {} it becomes an object. So my functions worked after removing those.
Also, I did not know you could console.log a function.

Learned both these things thanks to Carlos at the meetup today! :slight_smile:

1 Like

In case it helps, here is some more information about the differences:

If you have just one function to export, you can do it like this:

// filename: hello.js
function hello(name) {
    return `hello ${name}`;
}

module.exports = hello;

and then import it like this:

// import file hello.js
const hello = require("./hello");

hello("world"); //=> "hello world"

but if your file has multiple functions, you can export them like this:

// filename: greetings.js
function hello(name) {
    return `hello ${name}`;
}

function goodbye(name) {
    return `goodbye ${name}`;
}

// Esperanto version
function saluton(nomo) {
    return `saluton ${nomo}`;
}

module.exports = {
    hello,
    goodbye,
    saluton,
};

and import them like this:

// import file greetings.js
const greetings = require("./greetings");

// use a prefix, since it's an object with functions on it
greetings.hello("Alice");
greetings.goodbye("Bob");
greetings.saluton("mondo");

or just import one or more of them individually like this in order to use them without the prefix:

// The names on the left should match keys on the exported object
// from the file greetings.js. This imports just 2 of the 3 functions.
const { hello, goodbye } = require("./greetings");

// refer to the functions without prefixes now, because they are just
// functions that were extracted (destructured) from the object
hello("Alice");
goodbye("Bob");
1 Like

If you export it as an object you can add more functions later. If you don’t want to use the prefix when calling the function, you can import them with the curly braces to destructure the function:

const { addFood } = require("./foodFunctions");

addFood("carrots");

Destructuring picks items out of objects and arrays. A simpler example that you can run in the browser is:

// this could be any object
var food = {
    foodName: "carrots",
    vitamins: "a lot",
    taste: "good",
};

// this extracts two key-value pairs from the object
var { foodName, taste } = food;

console.log("foodName", foodName); //=> "carrots"
console.log("taste", taste); //=> "good"

It works with arrays too:

var [a, b] = [1, 2];

console.log("a", a); //=> 1
console.log("b", b); //=> 2

Importing just one or a few functions from the exporting file can be done with that technique.

1 Like

Hmm, this is all pretty interesting. I think I just have to practice. I am slowly getting there.

Also getting my mind around destructuring!

Thanks!

1 Like

I have another question related to sharing code between server and client.
Basically when the page first renders, it is server side. But at that time, I also want to define global state that can be used and modified later by the client.

Right now, I just have handlebars render that with the following code:

<script>
var globalJSON = {{{JSON_FROM_SERVER}}}; 
</script>

This is working for now, but I am worried about it breaking after modification and tree shaking. What is the best way to pass this data to the client?

My hands are hurting a bit today so it’s hard to type at the moment but I could look on a call if you want. If they are feeling better later or tomorrow I can type a reply then.

1 Like

No worries, I will message you about setting up a Jit.si a bit later today then. Thanks! :slight_smile:
Hope you feel better.

1 Like