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.