Node.js in Practice (2015)
Part 1. Node fundamentals
Node has an extremely small standard library intended to provide the lowest-level API for module developers to build on. Even though it’s relatively easy to find third-party modules, many tasks can be accomplished without them. In the chapters to follow, we’ll take a deep dive into a number of core modules and explore how to put them to practical use.
By strengthening your understanding of these modules, you’ll in turn become a more well-rounded Node programmer. You’ll also be able to dissect third-party modules with more confidence and understanding.
Chapter 1. Getting started
This chapter covers
· Why Node?
· Node’s main features
· Building a Node application
This book is geared toward intermediate Node developers, so this chapter is a quick refresher. If you want a thorough treatment of Node’s basics, then see our companion book, Node.js in Action (by Mike Cantelon, Marc Harter, TJ Holowaychuk, and Nathan Rajlich; Manning Publications, 2013).
In this chapter we’ll introduce Node, what it is, how it works, and why it’s something you can’t live without. In chapter 2 you’ll get to try out some techniques by looking at Node’s globals—the objects and methods available to every Node process.
Node In Practice is a recipe-style book, aimed at intermediate and advanced Node developers. Although this chapter covers some introductory material, later chapters advance quickly. For a beginner’s introduction to Node, see our companion book, Node.js in Action.
1.1. Getting to know Node
But first: why use Node, and when should you use it? Let’s look into that question by seeing what kinds of scenarios Node excels at.
1.1.1. Why Node?
1 See https://nodejstools.codeplex.com/.
Figure 1.1. An advertising server built with Node
In figure 1.1, a new HTTP request has been received and parsed by Node’s http module . The ad server’s application code then makes a database query, using an asynchronous API—a callback passed to a database read function . While Node waits for this to finish, the ad server is able to read a template file from the disk . This template will be used to display a suitable web page. Once the database request has finished, the template and database results are used to render the response .
Node isn’t limited to web development. You can create any kind of TCP/IP server that you like. For example, a network game server that broadcasts the game’s state to various players over TCP/IP sockets can perform background tasks, perhaps maintaining the game world, while it sends data to the players. Chapter 7 explores Node’s networking APIs.
When to use Node
To get you thinking like a true Nodeist, the table below has examples of applications where Node is a good fit.
· Efficiently distributes small pieces of information
· Handles potentially slow network connections
· Easily scales up to multiple processors or servers
· Programs a server catering to specific networking requirements without using C
Content management system, blog
· Easy to make RESTful JSON APIs
1.1.2. Node’s main features
Node’s main features are its standard library, module system, and npm. Of course, there’s more to it than that, but in this book we’ll focus on teaching you how to use these parts of Node. We’ll use third-party libraries where it’s considered best practice, but you’ll see a lot of Node’s built-in features.
In fact, Node’s strongest and most powerful feature is its standard library. This is really two parts: a set of binary libraries and the core modules. The binary libraries include libuv, which provides a fast run loop and non-blocking I/O for networking and the file system. It also has an HTTP library, so you can be sure your HTTP clients and servers are fast.
Figure 1.2 is a high-level overview of Node’s internals that shows how everything fits into place.
Figure 1.2. Node’s key parts in context
The next few sections focus on each core module in more detail, starting with the events API.
EventEmitter: An API for events
Sooner or later every Node developer runs into EventEmitter. At first it seems like something only library authors would need to use, but it’s actually the basis for most of Node’s core modules. The streams, networking, and file system APIs derive from it.
You can inherit from EventEmitter to make your own event-based APIs. Let’s say you’re working on a PayPal payment-processing module. You could make it event-based, so instances of Payment objects emit events like paid and refund. By designing the class this way, you decouple it from your application logic, so you can reuse it in more than one project.
We have a whole chapter dedicated to events: see chapter 4 for more. Another interesting part of EventEmitter is that it’s used as the basis for the stream module.
stream: The basis for scalable I/O
Streams inherit from EventEmitter and can be used to model data with unpredictable throughput—like a network connection where data speeds can vary depending on what other users on the network are doing. Using Node’s stream API allows you to create an object that receives events about the connection: data for when new data comes in, end when there’s no more data, and error when errors occur.
We love streams, so we’ve dedicated a whole chapter to them. Skip to chapter 5 to dive right in. You might think that events and streams sound abstract, and though that’s true, it’s also interesting to note that they’re used as a basis for I/O modules, like fs and net.
fs: Working with files
Node’s file system module is capable of reading and writing files using non-blocking I/O, but it also has synchronous methods. You can get information about files with fs.stat, and the synchronous equivalent is fs.statSync.
If you want to use streams to process the contents of a file in a super-efficient manner, then use fs.createReadStream to return a ReadableStream object. There’s more about this in chapter 6.
net: Create network clients and servers
The networking module is the basis for the http module and can be used to create generalized network clients and servers. Although Node development is typically thought of as web-based, chapter 7 shows you how to create TCP and UDP servers, which means you’re not limited to HTTP.
Global objects and other modules
If you have some experience making web applications with Node, perhaps with the Express framework, then you’ve already been using the http, net, and fs core modules without necessarily realizing it. Other built-in features aren’t headline-grabbing, but are critical to creating programs with Node.
Node’s module system is also part of this global functionality. Chapter 2 is packed with techniques that show you how to use these features.
Now that you’ve seen some of the core modules, it’s time to see them in action. The example will use the stream module to generate statistics on streams of text, and you’ll be able to use it with files and HTTP connections. If you want to learn more about the basics behind streams or HTTP in Node, refer to Node.js in Action.
1.2. Building a Node application
Instead of wading through more theory, we’ll show you how to build a Node application. It’s not just any application, though: it uses some of Node’s key features, like modules and streams. This will be a fast and intense tour of Node, so start up your favorite text editor and terminal and get ready.
Here’s what you’ll learn over the next 10 minutes:
· How to create a new Node project
· How to write your own stream class
· How to write a simple test and run it
Streams are great for processing data, whether you’re reading, writing, or transforming it. Imagine you want to convert data from a database into another format, like CSV. You could create a stream class that accepts input from a database and outputs it as a stream of CSV. The output of this new CSV stream could be connected to an HTTP request, so you could stream CSV directly to a browser. The same class could even be connected to a writable file stream—you could even fork the stream to create a file and send it to a web browser.
In this example, the stream class will accept text input, count word matches based on a regular expression, and then emit the results in an event when the stream has finished being sent. You could use this to count word matches in a text file, or pipe data from a web page and count the number of paragraph tags—it’s up to you. First we need to create a new project.
1.2.1. Creating a new Node project
When to use a package.json file
You may have an idea for a small script, and may be wondering if a package.json file is really necessary. It isn’t always necessary, but in general you should create them as often as possible.
Node developers prefer small modules, and expressing dependencies in package .json means your project, no matter how small, is super-easy to install in the future, or on another person’s machine.
1.2.2. Making a stream class
Create a new file called countstream.js and use util.inherits to derive from stream.Writable and implement the required _write method. Too fast? Let’s slow down. The full source is in the following listing.
Listing 1.1. A writable stream that counts
This example illustrates how subsequent examples in this book work. We present a snippet of code, annotated with hints on the underlying code. For example, the first part of the class uses the util.inherits method to inherit from the Writable base class . This example won’t be fully fleshed-out here—for more on writing your own streams, see technique 30 in chapter 5. For now, just focus on how regular expressions are passed to the constructor and used to count text as it flows into instances of the class . Node’s Writable class calls _write for us, so we don’t need to worry about that yet.
Streams and events
In listing 1.1 there was an event, total. This is one we made up—you can make up your own as well. Streams inherit from EventEmitter, so they have the same emit and on methods.
Node’s Writable base class will also call end when there’s no more data . This stream can be instantiated and piped as required. In the next section you’ll see how to connect it using pipe.
1.2.3. Using a stream
Now that you’ve seen how to make a stream class, you’re probably dying to try it out. Make another file, index.js, and add the code shown in the next listing.
Listing 1.2. Using the CountStream class
You can run this example by typing node index.js. It should display something like Total matches: 24. You can experiment with it by changing the URL that it fetches.
This example loads the module from listing 1.1 and then instantiates it with the text 'book' . It also downloads the text from a website using Node’s standard http module and then pipes the result through our CountStream class .
The significant thing here is res.pipe(countStream). When you pipe data, it doesn’t matter how big it is or if the network is slow: the CountStream class will dutifully count matches until the data has been processed. This Node program does not download the entire file first! It takes the file—piece by piece—and processes it. That’s the big thing here, and a critical aspect to Node development.
Figure 1.3. The three steps to creating a new Node project
Another important part of Node development is testing. The next section wraps up this example by testing CountStream.
1.2.4. Writing a test
We can write a short test for CountStream without using any third-party modules. Node comes with a built-in assert module, so we can use that for a quick test. Open test.js and add the code shown next.
Listing 1.3. Using the CountStream class
This test can be run with node test.js, and you should see Assertions passed: 1 printed in the console. The test actually reads the current file and passes the data through CountStream. It might invoke Ouroboros, but it’s a useful example because it gives us content that we know something about—we can always be sure there is one match for the word example.
Node comes with an assertion library called assert. A basic test can be made by calling the module directly – assert(expression).
The first thing the test does is listen for the total event, which is emitted by instances of CountStream . This is a good place to assert that the number of matches should be the same as what is expected . A readable stream that represents the current file is opened and piped through our class . Just before the end of the program, we print out how many assertions were hit .
This is important because if the total event never fires, then assert.equal won’t run at all. We have no way of knowing whether tests in callbacks are run, so a simple counter has been used to illustrate how Node programming can require patterns from the other programming languages and platforms that you might be familiar with.
If you’re getting tired, you can rest here, but there’s a bit of sugar to finish off our project. Node developers like to run tests and other scripts using npm on the command line. Open package.json and change the "test" property to look like this:
"test": "node test.js"
Now you can run tests just by typing npm test. This comes in handy when you have lots of tests and running them is more complicated. Running tests, test runners, and asynchronous testing issues are all covered in chapter 10.
The npm test and npm start commands can be configured by editing package.json. You can also run arbitrary commands, which are invoked with npm run command. All you need to do is set a property under scripts, just like listing 1.4.
This is useful for specific types of tests or housekeeping routines—for example npm run integration-tests, or maybe even npm run seed-data.
Depending on your previous experience with Node, this example might have been intense, but it captures how Node developers think and take advantage of the powerful resources that come with Node.
Now that you’ve seen how a Node project is put together, we’re done with the refresher course on Node. The next chapter introduces our first set of techniques, which is the bulk of this book’s format. It covers ways of working with the global features that are available to all Node programs.
In this chapter you’ve learned about Node.js in Practice—what it covers and how it focuses on Node’s impressive built-in core modules like the networking module and file system modules.
You’ve also learned about what makes Node tick, and how to use it. Some of the main points we covered were
· Node’s standard library is referred to as its core modules.
· What the core modules do—I/O tasks like network protocols, and work with files and more generic features like streams.
· How to quickly start a new Node project, complete with a package.json file so dependencies and scripts can be added.
· How to use Node’s powerful stream API to process data.
· Streams inherit from EventEmitter, so you can emit and respond to any events that you want to use in your application.
· How to write small tests just by using npm and the assert module—you can test out ideas without installing any third-party libraries.
Finally, we hope you learned something from our introductory application. Using event-based APIs, non-blocking I/O, and streams is really what Node is all about, but it’s also important to take advantage of Node’s unique tools like the package.json file and npm.
Now it’s time for techniques. The next chapter introduces the features that you don’t even have to load to use: the global objects.