Skip to content

Instantly share code, notes, and snippets.

@suissa
Created January 20, 2014 05:47
Show Gist options
  • Save suissa/8515512 to your computer and use it in GitHub Desktop.
Save suissa/8515512 to your computer and use it in GitHub Desktop.
Artigo sobre módulos em Node.js para ser traduzido. http://bites.goodeggs.com/posts/export-this/
<div class="entry-content"><p>When you require a module in Node, what are you getting back? When you write a module, what options do you have for designing its interface?</p>
<p>When I was first learning to work in Node I found the sheer number of ways to do things to be a bit overwhelming. JavaScript is extremely flexible and the community of developers contributing to open source seem to have different styles for implementing the same thing.</p>
<p>On my journey with Node I've been keeping an eye out the Good Way to do things and adopting them for use in my own work and in our work at Good Eggs.</p>
<p>In this post I'll share my observations of the Node module system and the ways in which you can use it to encapsulate and share code. My goal is to identify and illustrate useful patterns for module interface design and to help you understand when and how to use them in your own work.</p>
<p>I discuss seven patterns below, many of which can be used in combination. They are:</p>
<ul>
<li><a href="../../posts/export-this#namespace">Exports a Namespace</a></li>
<li><a href="../../posts/export-this#function">Exports a Function</a></li>
<li><a href="../../posts/export-this#higher_order_function">Exports a Higher Order Function</a></li>
<li><a href="../../posts/export-this#constructor">Exports a Constructor</a></li>
<li><a href="../../posts/export-this#singleton">Exports a Singleton</a></li>
<li><a href="../../posts/export-this#global_object">Extends a Global Object</a></li>
<li><a href="../../posts/export-this#monkey_patch">Applies a Monkey Patch</a></li>
</ul>
<!-- more -->
<h2>require, exports and module.exports</h2>
<p>First some fundamentals.</p>
<p>In Node requiring a file is requiring the module it defines. All modules have a reference to an implicit <code>module</code> object whose property <code>module.exports</code> is what is returned when you call <code>require</code>. A reference to <code>module.exports</code> is also available as <code>exports</code>.</p>
<p>It's as if there were an implicit line at the beginning of each module that reads:</p>
<pre class="highlighted"><code class="delphi"><span class="keyword">var</span> <span class="keyword">exports</span> = module.<span class="keyword">exports</span> = <span class="comment">{}</span>;</code></pre>
<p>If you want to export a function, you have to assign it to <code>module.exports</code>. Assigning a function to <code>exports</code> would just reassign the <code>exports</code> reference but <code>module.exports</code> would still point at the original empty object.</p>
<p>So we can define a module <code>function.js</code> that exports a function:</p>
<pre class="highlighted"><code class="matlab"><span class="transposed_variable">module.</span>exports = <span class="function"><span class="keyword">function</span> <span class="params">()</span> {</span>
<span class="keyword">return</span> <span class="cell">{name: <span class="string">'Jane'</span>}</span>;
};</code></pre>
<p>and require it with:</p>
<pre class="highlighted"><code class="php"><span class="keyword">var</span> func = <span class="keyword">require</span>(<span class="string">'./function'</span>);</code></pre>
<p>An important behavior of <code>require</code> is that it caches the value of <code>module.exports</code> and returns that same value for all future calls to <code>require</code>. It caches based on the absolute file path of the required file. So if you want your module to be able to return different values, you should have it export a function that can then be invoked to return a new value.</p>
<p>To demonstrate with the Node REPL:</p>
<pre class="highlighted"><code class="ruby"><span class="variable">$ </span>node
&gt; f1 = <span class="keyword">require</span>(<span class="string">'/Users/alon/Projects/export_this/function'</span>);
[<span class="constant">Function</span>]
&gt; f2 = <span class="keyword">require</span>(<span class="string">'./function'</span>); <span class="regexp">//</span> <span class="constant">Same</span> location
[<span class="constant">Function</span>]
&gt; f1 === f2
<span class="keyword">true</span>
&gt; f1() === f2()
<span class="keyword">false</span></code></pre>
<p>You can see that <code>require</code> is returning the same function instance but that the objects returned by that function are different instances for each call.</p>
<p>For more detail on Node's module system <a href="http://nodejs.org/api/modules.html">the core docs</a> provide good detail and are worth a read.</p>
<p>And now on to the interface patterns.</p>
<p><a name="namespace"></a></p>
<h2>Exports a Namespace</h2>
<p>A simple and common pattern is to export an object with a number of properties, primarily but not limited to functions. This allows the code requiring the module to pull in a collection of related functionality under a single namespace.</p>
<p>When you require a module that exports a namespace, you'll usually either assign the entire namespace to a variable and use its members through that reference, or assign members directly to local variables:</p>
<pre class="highlighted"><code class="haskell"><span class="title">var</span> fs = require('fs'),
readFile = fs.readFile,
<span class="type">ReadStream</span> = fs.<span class="type">ReadStream</span>;
<span class="title">readFile</span>('./file.txt', function(err, <span class="typedef"><span class="keyword">data</span>) <span class="container">{
<span class="title">console</span>.<span class="title">log</span>("<span class="title">readFile</span> <span class="title">contents</span>: '%<span class="title">s'</span>", <span class="title">data</span>);
}</span>);</span>
<span class="title">new</span> <span class="type">ReadStream</span>('./file.txt').on('<span class="typedef"><span class="keyword">data</span>', function<span class="container">(<span class="title">data</span>)</span> <span class="container">{
<span class="title">console</span>.<span class="title">log</span>("<span class="type">ReadStream</span> <span class="title">contents</span>: '%<span class="title">s'</span>", <span class="title">data</span>);
}</span>);</span></code></pre>
<p>Here's what the <a href="https://github.com/joyent/node/blob/e5346932bcbc523489c9418b82fde31cb666ee99/lib/fs.js#L33"><code>fs</code> core module</a> is doing:</p>
<pre class="highlighted"><code class="nginx"><span class="title">var</span> fs = exports;</code></pre>
<p>It first assigns the local variable <code>fs</code> to the implicit exports object and then assigns function references to properties of <code>fs</code>. Because <code>fs</code> references <code>exports</code> and exports is the object you get when you call <code>require('fs')</code> anything assigned to <code>fs</code> will be available on the object you get from <code>require</code>.</p>
<pre class="highlighted"><code class="r">fs.readFile = <span class="keyword">function</span>(path, options, callback_) {
// <span class="keyword">...</span>
};</code></pre>
<p>Anything is fair game. It then exports a constructor:</p>
<pre class="highlighted"><code class="r">fs.ReadStream = ReadStream;
<span class="keyword">function</span> ReadStream(path, options) {
// <span class="keyword">...</span>
}
ReadStream.prototype.open = <span class="keyword">function</span>() {
// <span class="keyword">...</span>
}</code></pre>
<p>When exporting a namespace, you can assign properties to <code>exports</code> as the <code>fs</code> module does above, or assign a new object to <code>module.exports</code>.</p>
<pre class="highlighted"><code class="r">module.exports = {
version: <span class="string">'1.0'</span>,
doSomething: <span class="keyword">function</span>() {
//<span class="keyword">...</span>
}
}</code></pre>
<p>A common use of exporting a namespace is to export the root of another module so that one require statement gives the caller access to a number of other modules. At Good Eggs, we implement each of our domain models in a separate module that exports the model constructor (see <a href="#constructor">Exports a Constructor</a> below) and then have an index file in the directory where the models live that exports all of the models. This allows us to pull in our models under a <code>models</code> namespace.</p>
<pre class="highlighted"><code class="apache">var models = <span class="keyword">require</span>('./models'),
<span class="keyword">User</span> = models.<span class="keyword">User</span>,
Product = models.Product;</code></pre>
<p>For CoffeeScript users, <a href="http://coffeescript.org/#destructuring">destructuring assignment</a> make this even cleaner.</p>
<pre class="highlighted"><code class="apache">{<span class="keyword">User</span>, Product} = <span class="keyword">require</span> './models'</code></pre>
<p><code>index.js</code> might look like:</p>
<pre class="highlighted"><code class="apache">exports.<span class="keyword">User</span> = <span class="keyword">require</span>('./<span class="keyword">user</span>');
exports.Person = <span class="keyword">require</span>('./person');</code></pre>
<p>In reality, we use a small library that requires all sibling files and exports their modules with CamelCase names so the <code>index.js</code> file in our models directory actually reads:</p>
<pre class="highlighted"><code class="ruby"><span class="keyword">module</span>.exports = <span class="keyword">require</span>(<span class="string">'../lib/require_siblings'</span>)(__filename);</code></pre>
<p><a name="function"></a></p>
<h2>Exports a Function</h2>
<p>Another pattern is to export a function as the interface to a module. A common use of this pattern is to export a factory function that returns an object when invoked. We see this when using <a href="http://expressjs.com">Express.js</a>:</p>
<pre class="highlighted"><code class="php"><span class="keyword">var</span> express = <span class="keyword">require</span>(<span class="string">'express'</span>);
<span class="keyword">var</span> app = express();
app.get(<span class="string">'/hello'</span>, <span class="function"><span class="keyword">function</span> <span class="params">(req, res)</span> {</span>
res.send <span class="string">"Hi there! We're using Express v"</span> + express.version;
});</code></pre>
<p>The function exported by Express is used to create a new Express application. In your own use of this pattern, your factory function may take arguments used to configure or initialize the object returned.</p>
<p>To export a function, you must assign your function to module.exports. <a href="https://github.com/visionmedia/express/blob/2e68ddbae9cec2d0b22f48f35ef4da964f51949e/lib/express.js#L18">Express does</a>:</p>
<pre class="highlighted"><code class="r">exports = module.exports = createApplication;
<span class="keyword">...</span>
<span class="keyword">function</span> createApplication () {
<span class="keyword">...</span>
}</code></pre>
<p>It's assigning the <code>createApplication</code> function to <code>module.exports</code> and then to the implicit <code>exports</code> variable. Now <code>exports</code> is the function that the module exports.</p>
<p>Express also uses this exported function as a namespace:</p>
<pre class="highlighted"><code class="profile"><span class="filename">exports.version = '3.1.1';</span></code></pre>
<p>Note that there's nothing to stop us from using the exported function as a namespace that can expose references to other functions, constructors or objects serving as namespaces themselves.</p>
<p>When exporting a function, it is good practice to name the function so that it will show up in stack traces. Note the stack trace differences in these two examples:</p>
<pre class="highlighted"><code class="javascript"><span class="comment">// bomb1.js</span>
module.exports = <span class="function"><span class="keyword">function</span> <span class="params">()</span> {</span>
<span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">'boom'</span>);
};</code></pre>
<pre class="highlighted"><code class="javascript"><span class="comment">// bomb2.js</span>
module.exports = <span class="function"><span class="keyword">function</span> <span class="title">bomb</span><span class="params">()</span> {</span>
<span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">'boom'</span>);
};</code></pre>
<pre class="highlighted"><code class="r">$ node
&gt; bomb = <span class="keyword">require</span>(<span class="string">'./bomb1'</span>);
[Function]
&gt; bomb()
Error: boom
at module.exports (/Users/alon/Projects/export_this/bomb1.js:<span class="number">2</span>:<span class="number">9</span>)
at repl:<span class="number">1</span>:<span class="number">2</span>
<span class="keyword">...</span>
&gt; bomb = <span class="keyword">require</span>(<span class="string">'./bomb2'</span>);
[Function: bomb]
&gt; bomb()
Error: boom
at bomb (/Users/alon/Projects/export_this/bomb2.js:<span class="number">2</span>:<span class="number">9</span>)
at repl:<span class="number">1</span>:<span class="number">2</span>
<span class="keyword">...</span></code></pre>
<p>There are a couple specific cases of exporting a function that are worth calling out as distinct patterns.</p>
<p><a name="higher_order_function"></a></p>
<h2>Exports a Higher Order Function</h2>
<p>A higher-order function, or functor, is a function that takes one or more functions as an input and/or outputs a function. We're talking about the latter case - a function that returns a function.</p>
<p>Exporting a higher order function is a useful pattern when you want to return a function from your module but need to take input that controls the behavior of that function.</p>
<p><a href="http://www.senchalabs.org/connect/">Connect middleware</a> provide a lot of pluggable functionality for Express and other web frameworks. A middleware is a function that takes three arguments - <code>(req, res, next)</code>. The convention in connect middleware is to export a function that when called returns the middleware function. This allows the exported function to take arguments that can be used to configure the middleware and are available through closure scope to the middleware when it is handling a request.</p>
<p>For example, here's the connect <a href="http://www.senchalabs.org/connect/query.html"><code>query</code> middleware</a> used internally by Express to parse query string parameters into a an object available as <code>req.query</code>.</p>
<pre class="highlighted"><code class="php"><span class="keyword">var</span> connect = <span class="keyword">require</span>(<span class="string">'connect'</span>),
query = <span class="keyword">require</span>(<span class="string">'connect/lib/middleware/query'</span>);
<span class="keyword">var</span> app = connect();
app.<span class="keyword">use</span>(query({maxKeys: <span class="number">100</span>}));</code></pre>
<p>The <code>query</code> source looks like:</p>
<pre class="highlighted"><code class="lua">var qs = <span class="built_in">require</span>(<span class="string">'qs'</span>)
, parse = <span class="built_in">require</span>(<span class="string">'../utils'</span>).parseUrl;
<span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> <span class="title">query</span><span class="params">(options)</span></span>{
<span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">query</span><span class="params">(req, res, next)</span></span>{
<span class="keyword">if</span> (!req.query) {
req.query = ~req.url.indexOf(<span class="string">'?'</span>)
? qs.parse(parse(req).query, options)
: {};
}
<span class="built_in">next</span>();
};
};</code></pre>
<p>For every request handled by the <code>query</code> middleware, the <code>options</code> argument available through closure scope is passed along to Node's core <code>qs</code> (query string) module.</p>
<p>This is a common and very flexible pattern for module design and one you are likely to find very useful in your own work.</p>
<p><a name="constructor"></a></p>
<h2>Exports a Constructor</h2>
<p>We define classes in JavaScript with constructor functions and create instances of classes with the <code>new</code> keyword.</p>
<pre class="highlighted"><code class="delphi"><span class="function"><span class="keyword">function</span> <span class="title">Person</span><span class="params">(name)</span> <span class="comment">{
this.name = name;
}</span>
<span class="title">Person</span>.<span class="title">prototype</span>.<span class="title">greet</span> = <span class="title">function</span><span class="params">()</span> <span class="comment">{
return "Hi, I'm Jane.";
}</span>;</span>
<span class="keyword">var</span> person = new Person(<span class="string">'Jane'</span>);
console.log(person.greet()); <span class="comment">// prints: Hi, I'm Jane</span></code></pre>
<p>For this pattern implement a class-per-file and export the constructor to make your project organization clear and to make it easy for other developers to find the implementation of a class. At Good Eggs, we implement classes in files with underscore_names and assign them to CamelCase names.</p>
<pre class="highlighted"><code class="php"><span class="keyword">var</span> Person = <span class="keyword">require</span>(<span class="string">'./person'</span>);
<span class="keyword">var</span> person = <span class="keyword">new</span> Person(<span class="string">'Jane'</span>);</code></pre>
<p>The implementation might look like:</p>
<pre class="highlighted"><code class="delphi"><span class="function"><span class="keyword">function</span> <span class="title">Person</span><span class="params">(name)</span> <span class="comment">{
this.name = name;
}</span>
<span class="title">Person</span>.<span class="title">prototype</span>.<span class="title">greet</span> = <span class="title">function</span><span class="params">()</span> <span class="comment">{
return "Hi, I'm " + this.name;
}</span>;</span>
module.<span class="keyword">exports</span> = Person;</code></pre>
<p><a name="singleton"></a></p>
<h2>Exports a Singleton</h2>
<p>Export a <a href="http://en.wikipedia.org/wiki/Singleton_pattern">singleton</a> when you want all users of your module to share the state and behavior of a single class instance.</p>
<p><a href="http://mongoosejs.com">Mongoose</a> is an object-document mapping library used to create rich domain models persisted in MongoDB.</p>
<pre class="highlighted"><code class="r">var mongoose = <span class="keyword">require</span>(<span class="string">'mongoose'</span>);
mongoose.connect(<span class="string">'mongodb://localhost/test'</span>);
var Cat = mongoose.model(<span class="string">'Cat'</span>, { name: String });
var kitty = new Cat({ name: <span class="string">'Zildjian'</span> });
kitty.save(<span class="keyword">function</span> (err) {
<span class="keyword">if</span> (err) // <span class="keyword">...</span>
console.log(<span class="string">'meow'</span>);
});</code></pre>
<p>What is that <code>mongoose</code> object we get back when we require Mongoose? Internally, the <code>mongoose</code> module is doing:</p>
<pre class="highlighted"><code class="r"><span class="keyword">function</span> Mongoose() {
//<span class="keyword">...</span>
}
module.exports = exports = new Mongoose();</code></pre>
<p>Because <code>require</code> caches the value assigned to <code>module.exports</code>, all calls to <code>require('mongoose')</code> will return this same instance ensuring that it is a singleton in our application. Mongoose uses an object-oriented design to encapsulate and decouple functionality, maintain state and support readability and comprehension, but creates a simple interface to users by creating and exporting an instance of the Mongoose class.</p>
<p>It also uses this singleton instance as a namespace to make other constructors available if needed by the user, including the Mongoose constructor itself. You might use the <code>Mongoose</code> constructor to create additional instances of mongoose connecting to additional MongoDB databases.</p>
<p>Internally, Mongoose does:</p>
<pre class="highlighted"><code class="avrasm">Mongoose<span class="preprocessor">.prototype</span><span class="preprocessor">.Mongoose</span> = Mongoose<span class="comment">;</span></code></pre>
<p>So that you can do:</p>
<pre class="highlighted"><code class="php"><span class="keyword">var</span> mongoose = <span class="keyword">require</span>(<span class="string">'mongoose'</span>),
Mongoose = mongoose.Mongoose;
<span class="keyword">var</span> myMongoose = <span class="keyword">new</span> Mongoose();
myMongoose.connect(<span class="string">'mongodb://localhost/test'</span>);</code></pre>
<p><a name="global_object"></a></p>
<h2>Extends a Global Object</h2>
<p>A required module can do more than just export a value. It can also modify global objects or objects returned when requiring other modules. It can define new global objects. It can just do this or do this in addition to exporting something useful.</p>
<p>Use this pattern when you need to extend or alter the behavior of global objects to provide the behavior delivered by your module. While certainly controversial and to be used judiciously (especially in open source work), this pattern can also be indispensable.</p>
<p><a href="https://github.com/visionmedia/should.js">Should.js</a> is an assertion library designed to be used in unit testing:</p>
<pre class="highlighted"><code class="matlab">require(<span class="string">'should'</span>);
var user = <span class="cell">{
name: <span class="string">'Jane'</span>
}</span>;
<span class="transposed_variable">user.</span><span class="transposed_variable">name.</span><span class="transposed_variable">should.</span>equal(<span class="string">'Jane'</span>);</code></pre>
<p>Should.js <a href="https://github.com/visionmedia/should.js/blob/68000f47d01408cacb80441a1d9bf10ba423e54c/lib/should.js#L107-L113">extends Object with a non-enumerable property <code>should</code></a> to provide a clean syntax for writing unit test asserts. Internally, <code>should.js</code> does:</p>
<pre class="highlighted"><code class="r">var should = <span class="keyword">function</span>(obj) {
<span class="keyword">return</span> new Assertion(util.isWrapperType(obj) ? obj.valueOf(): obj);
};
//<span class="keyword">...</span>
exports = module.exports = should;
//<span class="keyword">...</span>
Object.defineProperty(Object.prototype, <span class="string">'should'</span>, {
set: <span class="keyword">function</span>(){},
get: <span class="keyword">function</span>(){
<span class="keyword">return</span> should(this);
},
configurable: true
});</code></pre>
<p>Note that while Should.js exports the <code>should</code> function its primary use is through the <code>should</code> function it has added to <code>Object</code>.</p>
<p><a name="monkey_patch"></a></p>
<h2>Applies a Monkey Patch</h2>
<p>By <a href="http://en.wikipedia.org/wiki/Monkey_patch">monkey patch</a> I'm referring to "the dynamic modifications of a class or module at runtime, motivated by the intent to patch existing third-party code as a workaround to a bug or feature which does not act as desired."</p>
<p>Implement a module to patch an existing module when it doesn't provide an interface to customizing its behavior in the way you need. This pattern is a variant of the previous. Instead of modifying a global object, we are relying on the caching behavior of Node's module system to patch the same instance of a module that other code gets when it requires that module.</p>
<p>By default Mongoose names MongoDB collections by lowercasing and pluralizing the model name. For a model named <code>CreditCardAccountEntry</code> we'd end up with a collection named <code>creditcardaccountentries</code>. I prefer <code>credit_card_account_entries</code> and I want this behavior universally.</p>
<p>Here's the source for a module that patches <code>mongoose.model</code> when the module is required:</p>
<pre class="highlighted"><code class="php"><span class="keyword">var</span> Mongoose = <span class="keyword">require</span>(<span class="string">'mongoose'</span>).Mongoose;
<span class="keyword">var</span> _ = <span class="keyword">require</span>(<span class="string">'underscore'</span>);
<span class="keyword">var</span> model = Mongoose.prototype.model;
<span class="keyword">var</span> modelWithUnderScoreCollectionName = function(name, schema, collection, skipInit) {
collection = collection || _(name).chain().underscore().pluralize().value();
model.call(<span class="keyword">this</span>, name, schema, collection, skipInit);
};
Mongoose.prototype.model = modelWithUnderScoreCollectionName;</code></pre>
<p>When this module is required for the first time, it requires <code>mongoose</code>, redefines <code>Mongoose.prototype.model</code> and delegates back to the original implementation of <code>model</code>. Now all instances of <code>Mongoose</code> will have this new behavior. Note that it does not modify <code>exports</code> so the value returned to <code>require</code> will be the default empty <code>exports</code> object.</p>
<p>As a side note, if you do choose to monkey patch existing code, use a chaining technique similar to my example above. Add your behavior then delegate back to the original implementation. While not foolproof, it is the safest way to patch third party code allowing you to take advantage of future updates to the library and minimizing conflict with other patches that may be applied.</p>
<h2>Export Away!</h2>
<p>The Node module system provides a simple mechanism for encapsulating functionality and creating clear interfaces to your code. I hope the seven patterns here are a useful breakdown of different strategies available to you.</p>
<p>I haven't been exhaustive and there are certainly other options available but I have attempted to describe the most common and useful. Have I missed anything that should be included here?</p>
<p><em>Thanks to the incredibly prolific Node developer community for all the open source work from which I have done most of my learning. I encourage you to read the code of the libraries you are using and to find the great developers out there with clear, consistent and readable styles that can inspire your own. Special shout out to <a href="https://github.com/visionmedia">TJ Holowaychuk</a> whose work on Express.js, Connect and Should.js are referenced above.</em></p>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment