export vs exports in nodejs modules
In Node.js and JavaScript modules, export and exports serve different purposes and are used in different contexts. Here’s an explanation of each, along with examples to help clarify their usage:
export
The export keyword is used in the context of ES6 modules. It is part of the ECMAScript 2015 (ES6) module syntax and is used to export functions, objects, or primitives from a module so that they can be imported into other modules with the import keyword.
Syntax
Named Exports:
// module.js
export const foo = () => { ... };
export const bar = () => { ... };
// main.js
import { foo, bar } from './module.js';
foo();
bar();
Default Export:
// module.js
const myFunction = () => { ... };
export default myFunction;
// main.js
import myFunction from './module.js';
myFunction();
Combining Default and Named Exports:
// module.js
const mainFunction = () => { ... };
const auxiliaryFunction1 = () => { ... };
const auxiliaryFunction2 = () => { ... };
export default mainFunction;
export { auxiliaryFunction1, auxiliaryFunction2 };
// main.js
import mainFunction, { auxiliaryFunction1, auxiliaryFunction2 } from './module.js';
mainFunction();
auxiliaryFunction1();
auxiliaryFunction2();
module.exports and exports
In Node.js, which traditionally uses the CommonJS module system, module.exports and exports are used to export values from a module. They are not part of the ES6 module syntax but are specific to Node.js.
Syntax
Using module.exports:
// module.js
const myFunction = () => { ... };
module.exports = myFunction;
// main.js
const myFunction = require('./module.js');
myFunction();
Using exports:
// module.js
exports.foo = () => { ... };
exports.bar = () => { ... };
// main.js
const { foo, bar } = require('./module.js');
foo();
bar();
Note: exports is simply a reference to module.exports at the start of the module, but reassigning exports directly does not change module.exports.
Example
Correct usage of exports and module.exports:
// module.js
exports.foo = () => { ... };
exports.bar = () => { ... };
// OR
module.exports.foo = () => { ... };
module.exports.bar = () => { ... };
Incorrect usage that can cause issues:
// module.js
exports = {
foo: () => { ... },
bar: () => { ... }
};
// main.js
const myModule = require('./module.js');
console.log(myModule); // This will be an empty object because exports no longer refers to module.exports
Combining module.exports and exports
You can mix module.exports and exports, but it’s important to understand their relationship. exports is a shorthand for module.exports and should not be reassigned directly if you want to preserve the reference.
Example of combining:
// module.js
module.exports = () => { ... }; // default export
exports.foo = () => { ... }; // named export
exports.bar = () => { ... }; // named export
// main.js
const mainFunction = require('./module.js');
const { foo, bar } = mainFunction;
mainFunction();
foo();
bar();
Practical Example of Socket.IO Export
To understand how Socket.IO uses these concepts, here’s the relevant part of Socket.IO’s code:
const Server = require('./server'); // Assuming Server is defined in another file
const Namespace = require('./namespace');
const Socket = require('./socket');
module.exports = (srv, opts) => new Server(srv, opts);
module.exports.Server = Server;
module.exports.Namespace = Namespace;
module.exports.Socket = Socket;
In this example, module.exports is used to export an anonymous function as the default export. Additionally, module.exports.Server, module.exports.Namespace, and module.exports.Socket are used to export named members.