Use Module.exports to Keep Node.js Code Organized
Written by Jon Church and Joe Shindelar. Originally published on Medium.
Node doesn’t care if we write our entire project in one huge index.js file. But if you want people to be able to read your code, you need to organize it in a way that makes sense and is easy to maintain. Enter Node’s module system!
What exactly is a module? Modules organize related functions together into a file, that we can import later when we need to use them. A module encapsulates related code into a single unit, usually stored in a file. Every file in Node can be considered a module, and each has its own module
global object available in the file that we will use to expose code for importing in another file.
In this tutorial we’ll:
- Learn about
module.exports
- Export helper functions from a file
- Include our helper functions in another file using
require
By the end of this tutorial you should be able to use Node’s module system to create your own modules so you can reuse code in your project.
Goal
Import helper functions from helpers.js in another file, index.js.
What is module.exports?
To export code from a file, we assign values to the file's module.exports
object. Each JavaScript file in Node has a module.exports
object in its global scope that represents that specific file. That object will hold any code that we want to export from the file. We can assign an object, a single function, or any value to module.exports
to be used again in another file.
This is part of the module system in Node. Each file has a global object in its scope called module
, which holds information about that specific file. We can expose code from that file by assigning it to module.exports
.
What is require
?
Require helps us load modules. To import code from a file, we must pass the file’s path to require: require('./filepath')
. Calling require
on a JavaScript file will run the code in the file, and return the value of module.exports
.
This is actually very similar to what happens when you require
an npm module. Except in this instance, we are passing require
a path to a file instead of the name of a package. When you require
an npm package, the same thing is happening behind the scenes in your node_modules/ folder, where the package is installed to.
Here we export a string from a file called testModule.js
:
// testModule.js
module.exports = "This is a test!"
// index.js
const test = require('./testModule.js')
console.log(test) // => "This is a test!"
In any given file, we can assign things to module.exports
to allow us to import them in another file using a require
statement.
When the require
is called with a file path, the code in that file will be evaluated and module.exports
will be returned.
// helpers.js
console.log('I got required!)
module.exports = "Exported!"
// index.js
const exportedValue = require('./helpers.js')
The above code will output “I got required!” to the console, and exportedValue
will be equal to “Exported!”
.
If we rerun the require statement (after the first instance), we would still get the exported value of module.exports
, but the console.log
would not run again. This is because require statements are cached; they are run once, and then calling them again will just return the value of module.exports
.
You might also see ES6 code like this at some point:
// helpers.js
export default = "I'm an export!"
// index.js
import helpers from './helpers.js'
This uses ES6 module syntax, and currently no Node engine supports this by default. You can use this style if working with something like Babel that transpiles your code for you. But for now, know that we are talking about the CommonJS syntax of exports, module.exports
.
Creating a Helpers File
So let’s use module.exports
to export some functions from a helpers.js
file to do some simple math, and then use them in another file, index.js
, with the help of require
.
One option is to export a single anonymous function from a file.
Let’s export a single function that multiplies a number by 2 and use it in our index.js file:
// helpers.js
module.exports = function(x) {
return x * 2
}
// index.js
const helpers = require('./helpers.js')
helpers(4) // => 8
We set the value of module.exports
to the function we want to import, and require it in index.js.
Handy, but what if you want to export several functions at once? Well, we can export an object literal that holds several functions, then access the functions by their names. This is also called Named Exports, because we can choose to later import only the properties we want by name.
// helpers.js
module.exports = {
multiplyByTwo: function(x) { return x *2 },
divideByTwo: function(x) { return x / 2}
}
// index.js
const helpers = require('./helpers.js')
helpers.multiplyByTwo(10) // => 5
// or, you can import just the named property you need
const divideByTwo = require('./helpers.js').divideByTwo
divideByTwo(18) // => 9
Requiring the file returns the object we exported with module.exports
. You can also import only the property you need, by using dot syntax after the require statement.
Instead of having to declare the functions all in one place, we can make use of a different syntax. Here, we directly export a named property on the module.exports
object.
// helpers.js
module.exports.multiplyByTwo = function(x) { return x * 2 }
module.exports.divideByTwo = function(x) { return x / 2 }
function nonExportedFunction(x) {
return x * 3
}
// index.js
const helpers = require('./helpers.js/)
const divideByTwo = require('./helpers.js').divideByTwo
In the code above, module.exports
will look the same as the object we exported in the previous example. Both will be an object with two keys, multiplyByTwo
and divideByTwo
. The only difference is that we assigned them one at a time.
Note: Make sure you don’t mix the above syntax with exporting an object directly. If you assign something to module.exports
later in your file, it will overwrite anything you had already exported.
// helpers.js
module.exports.multiplyByTwo = function(x) { return x * 2 }
module.exports.divideByTwo = function(x) { return x / 2 }
module.exports = "I just overwrote all my exports! Don't mix these styles!"
Passing arguments to a module
We can have a little fun with our math helpers by using some currying. Currying is a concept from functional programming: you call a function with a variable and get another function back that has that variable baked in already. It’s a sort of configuration, if you will. That’s a simple way to put it, but it can be very powerful.
Let’s make our math functions center around a single number to do operations with. We can pass in that number when requiring the file, and get back functions that multiply, divide, or add with the number we initially passed to our curry function.
// curryHelpers.js
module.exports = function(x) {
return {
multiply: function(y) { return y * x },
divide: function(y) { return y / x },
add: function(y) { return y + x }
}
}
// index.js
const byTwo = require('./curryHelpers.js')(2)
const byTen = require('./curryHelpers.js')(10)
byTwo.multiply(5) // => 10
byTwo.divide(14) // => 7
byTwo.add(9) // => 11
byTen.multiply(5) // => 50
This is an example of passing a variable into a file’s scope using require
and module.exports
. We passed the number 2 into the curryHelpers file’s scope by calling the function returned by require
. The object of functions we exported are now configured to multiply, divide, and add any number passed to it by the initial number we passed in, 2. We did the same thing with the number 10, and got back an object of functions configured with the number 10.
exports
as shorthand for module.exports
Finally, a shorthand for module.exports
can be used. The exports
object is globally available as well, and points to the same object as module.exports
.
So the following code is equivalent:
// assigning named exports
module.exports.pizza = true
exports.pizza = true
// exporting an object
module.exports = {}
exports = {}
Wrapping up
Node is a very modular language, and you can take advantage of that yourself by using module.exports
to help export and reuse code in your project. Using require
, you can import that code in any other file in your project. The idea of importing modules is very common in Node and understanding module.exports
will help you navigate that modular approach.
I hope you’re able to use some of these patterns in your code!
If you want to learn more about module.exports
and how it works, you could learn about module scope and the CommonJS module system.
Lullabot Education is ramping up our Node.js training. Sign up for our newsletter and learn about upcoming guides and tutorials — and help shape the future of Node.js education.
Top comments (1)
Does
exports = {}
actually work?I thought that's when you MUST use
module.exports = {}
.