Node - JS Handbook
Node - JS Handbook
of Contents
The Node.js Handbook
Introduction
Introduction to Node
A brief history of Node
How to install Node
How much JavaScript do you need to know to use Node?
Differences between Node and the Browser
v8
Basics
Run Node.js scripts from the command line
How to exit from a Node.js program
How to read environment variables
Node hosting options
Command Line
Use the Node REPL
Pass arguments from the command line
Output to the command line
Accept input from the command line
Node modules and npm
Expose functionality from a Node file using exports
npm
Where does npm install the packages
How to use or execute a package installed using npm
The package.json file
The package-lock.json file
Find the installed version of an npm package
How to install an older version of an npm package
How to update all the Node dependencies to their latest version
Semantic versioning rules
Uninstalling npm packages
2
Global or local packages
npm dependencies and devDependencies
npx
Working with the event loop
The event loop
nextTick
setImmediate
Timers
Asynchronous programming
Callbacks
Promises
async/await
The Node Event Emitter
Networking
HTTP
How HTTP Requests work
Build an HTTP server
Making HTTP requests
Axios
Websockets
HTTPS, secure connections
File System
File descriptors
File stats
File paths
Reading files
Writing files
Working with folders
Some essential core modules
The fs module
The path module
The os module
The events module
3
The http module
Miscellaneous
Streams
Working with MySQL
Difference between development and production
4
The Node.js Handbook
The Node Handbook follows the 80/20 rule: learn in 20% of the time the 80% of a topic.
I find this approach gives a well-rounded overview. This book does not try to cover everything
under the sun related to Node. If you think some specific topic should be included, tell me.
I hope the contents of this book will help you achieve what you want: learn the basics
Node.js.
This book is written by Flavio. I publish web development tutorials every day on my website
flaviocopes.com.
Enjoy!
5
Introduction to Node
Introduction to Node
This post is a getting started guide to Node.js, the server-side JavaScript
runtime environment. Node.js is built on top of the Google Chrome V8
JavaScript engine, and it's mainly used to create web servers - but it's not
limited to that
Overview
The best features of Node.js
Fast
Simple
JavaScript
V8
Asynchronous platform
A huge number of libraries
An example Node.js application
Node.js frameworks and tools
Overview
Node.js is a runtime environment for JavaScript that runs on the server.
Node.js is open source, cross-platform, and since its introduction in 2009, it got hugely popular
and now plays a significant role in the web development scene. If GitHub stars are one
popularity indication factor, having 46000+ stars means being very popular.
6
Introduction to Node
Node.js is built on top of the Google Chrome V8 JavaScript engine, and it's mainly used to
create web servers - but it's not limited to that.
One of the main selling points of Node.js is speed. JavaScript code running on Node.js
(depending on the benchmark) can be twice as fast than compiled languages like C or Java,
and orders of magnitude faster than interpreted languages like Python or Ruby, because of its
non-blocking paradigm.
Simple
Node.js is simple. Extremely simple, actually.
JavaScript
Node.js runs JavaScript code. This means that millions of frontend developers that already
use JavaScript in the browser are able to run the server-side code and frontend-side code
using the same programming language without the need to learn a completely different tool.
The paradigms are all the same, and in Node.js the new ECMAScript standards can be used
first, as you don't have to wait for all your users to update their browsers - you decide which
ECMAScript version to use by changing the Node.js version.
V8
7
Introduction to Node
Running on the Google V8 JavaScript engine, which is Open Source, Node.js is able to
leverage the work of thousands of engineers that made (and will continue to make) the
Chrome JavaScript runtime blazing fast.
Asynchronous platform
In traditional programming languages (C, Java, Python, PHP) all instructions are blocking by
default unless you explicitly "opt in" to perform asynchronous operations. If you perform a
network request to read some JSON, the execution of that particular thread is blocked until the
response is ready.
JavaScript allows to create asynchronous and non-blocking code in a very simple way,
by using a single thread, callback functions and event-driven programming. Every time
an expensive operation occurs, we pass a callback function that will be called once we can
continue with the processing. We're not waiting for that to finish before going on with the rest
of the program.
Such mechanism derives from the browser. We can't wait until something loads from an AJAX
request before being able to intercept click events on the page. It all must happen in real
time to provide a good experience to the user.
If you've created an onclick handler for a web page you've already used asynchronous
programming techniques with event listeners.
This allows Node.js to handle thousands of concurrent connections with a single server
without introducing the burden of managing threads concurrency, which would be a major
source of bugs.
Node provides non-blocking I/O primitives, and generally, libraries in Node.js are written using
non-blocking paradigms, making a blocking behavior an exception rather than the normal.
8
Introduction to Node
When Node.js needs to perform an I/O operation, like reading from the network, access a
database or the filesystem, instead of blocking the thread Node.js will simply resume the
operations when the response comes back, instead of wasting CPU cycles waiting.
registry hosts almost 500.000 open source packages you can freely use.
To run this snippet, save it as a server.js file and run node server.js in your terminal.
Node.js has an amazing standard library, including a first-class support for networking.
The createServer() method of http creates a new HTTP server and returns it.
The server is set to listen on the specified port and hostname. When the server is ready, the
callback function is called, in this case informing us that the server is running.
Whenever a new request is received, the request event is called, providing two objects: a
request (an http.IncomingMessage object) and a response (an http.ServerResponse object).
The first provides the request details. In this simple example, this is not used, but you could
access the request headers and request data.
9
Introduction to Node
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
and we end close the response, adding the content as an argument to end() :
res.end('Hello World\n')
Many of those established over time as popular options. Here is a non-comprehensive list to
the ones I consider very relevant and worth learning:
Express, one of the most simple yet powerful ways to create a web server. Its minimalist
approach, unopinionated, focused on the core features of a server, is key to its success.
Meteor, an incredibly powerful full-stack framework, powering you with an isomorphic
approach to building apps with JavaScript, sharing code on the client and the server.
Once an off-the-shelf tool that provided everything, now integrates with frontend libs
React, Vue and Angular. Can be used to create mobile apps as well.
koa, built by the same team behind Express, aims to be even simpler and smaller,
building on top of years of knowledge. The new project born out of the need to create
incompatible changes without disrupting the existing community.
Next.js, a framework to render server-side rendered React applications.
Micro, a very lightweight server to create asynchronous HTTP microservices.
Socket.io, a real-time communication engine to build network applications.
10
A brief history of Node
In comparison, JavaScript is 23 years old and the web as we know it (after the introduction of
Mosaic) is 25 years old.
9 years is such a little amount of time for a technology, but Node.js seems to have been
around forever.
I've had the pleasure to work with Node since the early days when it was just 2 years old, and
despite the little information available, you could already feel it was a huge thing.
In this post, I want to draw the big picture of Node in its history, to put things in perspective.
Part of the business model of Netscape was to sell Web Servers, which included an
environment called Netscape LiveWire, which could create dynamic pages using server-side
JavaScript. So the idea of server-side JavaScript was not introduced by Node.js, but it's old
just like JavaScript - but at the time it was not successful.
One key factor that led to the rise of Node.js was timing. JavaScript since a few years was
starting being considered a serious language, thanks for the "Web 2.0" applications that
showed the world what a modern experience on the web could be like (think Google Maps or
11
A brief history of Node
GMail).
The JavaScript engines performance bar raised considerably thanks to the browser
competition battle, which is still going strong. Development teams behind each major browser
work hard every day to give us better performance, which is a huge win for JavaScript as a
platform. V8, the engine that Node.js uses under the hood, is one of those and in particular it's
the Chrome JS engine.
But of course, Node.js is not popular just because of pure luck or timing. It introduced much
innovative thinking on how to program in JavaScript on the server.
2009
Node.js is born The first form of npm is created
2010
Express is born Socket.io is born
2011
npm hits 1.0 Big companies start adopting Node: LinkedIn, Uber Hapi is born
2012
Adoption continues very rapidly
2013
First big blogging platform using Node: Ghost Koa is born
2014
Big drama: IO.js is a major fork of Node.js, with the goal of introducing ES6 support and move
faster
2015
12
A brief history of Node
The Node.js Foundation is born IO.js is merged back into Node.js npm introduces private
modules Node 4 (no 1, 2, 3 versions were previously released)
2016
The leftpad incident Yarn is born Node 6
2017
npm focuses more on security Node 8 HTTP/2 V8 introduces Node in its testing suite, officially
making Node a target for the JS engine, in addition to Chrome 3 billion npm downloads every
week
2018
Node 10 ES modules .mjs experimental support
13
How to install Node
Node.js can be installed in different ways. This post highlights the most common and
convenient ones.
Official packages for all the major platforms are available at https://nodejs.org/en/download/.
One very convenient way to install Node.js is through a package manager. In this case, every
operating system has its own.
On macOS, Homebrew is the de-facto standard, and - once installed - allows to install Node.js
very easily, by running this command in the CLI:
nvm is a popular way to run Node. It allows you to easily switch the Node version, and install
new versions to try and easily rollback if something breaks, for example.
It is also very useful to test your code with old Node versions.
My suggestion is to use the official installer if you are just starting out and you don't use
Homebrew already, otherwise, Homebrew is my favorite solution.
In any case, when Node is installed you'll have access to the node executable program in the
command line.
14
How much JavaScript do you need to know to use Node?
As a beginner, it's hard to get to a point where you are confident enough in your programming
abilities.
While learning to code, you might also be confused at where does JavaScript end, and where
Node.js begins, and vice versa.
I would recommend you to have a good grasp of the main JavaScript concepts before diving
into Node.js:
Lexical Structure
Expressions
Types
Variables
Functions
this
Arrow Functions
Loops
Loops and Scope
Arrays
Template Literals
Semicolons
Strict Mode
ECMAScript 6, 2016, 2017
With those concepts in mind, you are well on your road to become a proficient JavaScript
developer, in both the browser and in Node.js.
The following concepts are also key to understand asynchronous programming, which is one
fundamental part of Node.js:
15
How much JavaScript do you need to know to use Node?
Luckily I wrote a free ebook that explains all those topics, and it's called JavaScript
Fundamentals. It's the most compact resource you'll find to learn all of this.
You can find the ebook at the bottom of this page: https://flaviocopes.com/javascript/.
16
Differences between Node and the Browser
Both the browser and Node use JavaScript as their programming language.
Building apps that run in the browser is a completely different thing than building a Node.js
application.
Despite the fact that it's always JavaScript, there are some key differences that make the
experience radically different.
As a frontend developer that writes Node apps have a huge advantage - the language is still
the same.
You have a huge opportunity because we know how hard it is to fully, deeply learn a
programming language, and by using the same language to perform all your work on the web -
both on the client and on the server, you're in a unique position of advantage.
In the browser, most of the time what you are doing is interacting with the DOM, or other Web
Platform APIs like Cookies. Those do not exist in Node, of course. You don't have the
document , window and all the other objects that are provided by the browser.
And in the browser, we don't have all the nice APIs that Node.js provides through its modules,
like the filesystem access functionality.
Another big difference is that in Node.js you control the environment. Unless you are building
an open source application that anyone can deploy anywhere, you know which version of
Node you will run the application on. Compared to the browser environment, where you don't
get the luxury to choose what browser your visitors will use, this is very convenient.
This means that you can write all the modern ES6-7-8-9 JavaScript that your Node version
supports.
Since JavaScript moves so fast, but browsers can be a bit slow and users a bit slow to
upgrade, sometimes on the web, you are stuck to use older JavaScript / ECMAScript releases.
YYou can use Babel to transform your code to be ES5-compatible before shipping it to the
browser, but in Node, you won't need that.
Another difference is that Node uses the CommonJS module system, while in the browser we
are starting to see the ES Modules standard being implemented.
17
Differences between Node and the Browser
In practice, this means that for the time being you use require() in Node and import in the
browser.
18
v8
v8
V8 is the name of the JavaScript engine that powers Google Chrome. It's the
thing that takes our JavaScript and executes it while browsing with Chrome.
V8 provides the runtime environment in which JavaScript executes. The DOM
and the other Web Platform APIs are provided by the browser.
V8 is the name of the JavaScript engine that powers Google Chrome. It's the thing that takes
our JavaScript and executes it while browsing with Chrome.
V8 provides the runtime environment in which JavaScript executes. The DOM, and the other
Web Platform APIs are provided by the browser.
The cool thing is that the JavaScript engine is independent by the browser in which it's hosted.
This key feature enabled the rise of Node.js. V8 was chosen for being the engine chosen by
Node.js back in 2009, and as the popularity of Node.js exploded, V8 became the engine that
now powers an incredible amount of server-side code written in JavaScript.
The Node.js ecosystem is huge and thanks to it V8 also powers desktop apps, with projects
like Electron.
19
v8
Other JS engines
Other browsers have their own JavaScript engine:
All those engines implement the ECMA ES-262 standard, also called ECMAScript, the
standard used by JavaScript.
In this V8 introduction, I will ignore the implementation details of V8: they can be found on
more authoritative sites (e.g. the V8 official site), and they change over time, often radically.
V8 is always evolving, just like the other JavaScript engines around, to speed up the Web and
the Node.js ecosystem.
On the web, there is a race for performance that's been going on for years, and we (as users
and developers) benefit a lot from this competition because we get faster and more optimized
machines year after year.
Compilation
JavaScript is generally considered an interpreted language, but modern JavaScript engines no
longer just interpret JavaScript, they compile it.
This happens since 2009 when the SpiderMonkey JavaScript compiler was added to Firefox
3.5, and everyone followed this idea.
This might seem counter-intuitive, but since the introduction of Google Maps in 2004,
JavaScript has evolved from a language that was generally executing a few dozens of lines of
code to complete applications with thousands to hundreds of thousands of lines running in the
browser.
20
v8
Our applications now can run for hours inside a browser, rather than being just a few form
validation rules or simple scripts.
In this new world, compiling JavaScript makes perfect sense because while it might take a little
bit more to have the JavaScript ready, once done it's going to be much more performant that
purely interpreted code.
21
Run Node.js scripts from the command line
The usual way to run a Node program is to call the node globally available command (once
you install Node) and pass the name of the file you want to execute.
If your main Node application file is in app.js , you can call it by typing
node app.js
22
How to exit from a Node.js program
When running a program in the console you can close it with ctrl-C , but what I want to
discuss here is programmatically exiting.
Let's start with the most drastic one, and see why you're better off not using it.
The process core module is provides a handy method that allows you to programmatically
exit from a Node.js program: process.exit() .
When Node.js runs this line, the process is immediately forced to terminate.
This means that any callback that's pending, any network request still being sent, any
filesystem access, or processes writing to stdout or stderr - all is going to be ungracefully
terminated right away.
If this is fine for you, you can pass an integer that signals the operating system the exit code:
process.exit(1)
By default the exit code is 0 , which means success. Different exit codes have different
meaning, which you might want to use in your own system to have the program communicate
to other programs.
process.exitCode = 1
and when the program will later end, Node will return that exit code.
Many times with Node we start servers, like this HTTP server:
23
How to exit from a Node.js program
This program is never going to end. If you call process.exit() , any currently pending or
running request is going to be aborted. This is not nice.
In this case you need to send the command a SIGTERM signal, and handle that with the
process signal handler:
process.on('SIGTERM', () => {
server.close(() => {
console.log('Process terminated')
})
})
What are signals? Signals are a POSIX intercommunication system: a notification sent to
a process in order to notify it of an event that occurred.
SIGKILL is the signals that tells a process to immediately terminate, and would ideally act like
process.exit() .
SIGTERM is the signals that tells a process to gracefully terminate. It is the signal that's sent
You can send this signal from inside the program, in another function:
process.kill(process.pid, 'SIGTERM')
Or from another Node.js running program, or any other app running in your system that knows
the PID of the process you want to terminate.
24
How to read environment variables
The process core module of Node provides the env property which hosts all the
environment variables that were set at the moment the process was started.
Here is an example that accesses the NODE_ENV environment variable, which is set to
development by default.
process.env.NODE_ENV // "development"
Setting it to "production" before the script runs will tell Node that this is a production
environment.
In the same way you can access any custom environment variable you set.
25
Node hosting options
Here is a non-exhaustive list of the options you can explore when you want to deploy your app
and make it publicly accessible.
I will list the options from simplest and constrained to more complex and powerful.
This option is suited for some quick testing, demo a product or sharing of an app with a very
small group of people.
Using it, you can just type ngrok PORT and the PORT you want is exposed to the internet. You
will get a ngrok.io domain, but with a paid subscription you can get a custom URL as well as
more security options (remember that you are opening your machine to the public Internet).
26
Node hosting options
Glitch
Glitch is a playground and a way to build your apps faster than ever, and see them live on their
own glitch.com subdomain. You cannot currently have a a custom domain, and there are a few
restrictions in place, but it's really great to prototype. It looks fun (and this is a plus), and it's
not a dumbed down environment - you get all the power of Node.js, a CDN, secure storage for
credentials, GitHub import/export and much more.
Provided by the company behind FogBugz and Trello (and co-creators of Stack Overflow).
Codepen
Codepen is an amazing platform and community. You can create a project with multiple files,
and deploy it with a custom domain.
Serverless
A way to publish your apps, and have no server at all to manage, is Serverless. Serverless is a
paradigm where you publish your apps as functions, and they respond on a network endpoint
(also called FAAS - Functions As A Service).
Serverless Framework
Standard Library
They both provide an abstraction layer to publishing on AWS Lambda and other FAAS
solutions based on Azure or the Google Cloud offering.
PAAS
PAAS stands for Platform As A Service. These platforms take away a lot of things you should
otherwise worry about when deploying your application.
Zeit Now
Zeit is an interesting option. You just type now in your terminal, and it takes care of deploying
your application. There is a free version with limitations, and the paid version is more powerful.
You simply forget that there's a server, you just deploy the app.
27
Node hosting options
Nanobox
Nanobox
Heroku
Heroku is an amazing platform.
Microsoft Azure
Azure is the Microsoft Cloud offering.
Digital Ocean
Linode
Amazon Web Services, in particular I mention Amazon Elastic Beanstalk as it abstracts
away a little bit the complexity of AWS.
Since they provide an empty Linux machine on which you can work, there is no specific tutorial
for these.
There are lots more options in the VPS category, those are just the ones I used and I would
recommend.
Bare metal
Another solution is to get a bare metal server, install a Linux distribution, connect it to the
internet (or rent one monthly, like you can do using the Vultr Bare Metal service)
28
Node hosting options
29
Use the Node REPL
The node command is the one we use to run our Node.js scripts:
node script.js
node
❯ node
>
the command stays in idle mode and waits for us to enter something.
Tip: if you are unsure how to open your terminal, google "How to open terminal on ".
The REPL is waiting for us to enter some JavaScript code, to be more precise.
> console.log('test')
test
undefined
>
The first value, test , is the output we told the console to print, then we get undefined which
is the return value of running console.log() .
As you write your code, if you press the tab key the REPL will try to autocomplete what you
wrote to match a variable you already defined or a predefined one.
30
Use the Node REPL
The REPL will print all the properties and methods you can access on that class:
31
Use the Node REPL
Dot commands
The REPL has some special commands, all starting with a dot . . They are
.editor : enables editor more, to write multiline JavaScript code with ease. Once you are
32
Use the Node REPL
.save : saves all you entered in the REPL session to a file (specify the filename)
The REPL knows when you are typing a multi-line statement without the need to invoke
.editor .
and you press enter , the REPL will go to a new line that starts with 3 dots, indicating you can
now continue to work on that block.
... console.log(num)
... })
If you type .break at the end of a line, the multiline mode will stop and the statement will not
be executed.
33
Pass arguments from the command line
You can pass any number of arguments when invoking a Node.js application using
node app.js
For example:
or
This changes how you will retrieve this value in the Node code.
The way you retrieve it is using the process object built into Node.
It exposes an argv property, which is an array that contains all the command line invocation
arguments.
The second element is the full path of the file being executed.
All the additional arguments are present from the third position going forward.
You can iterate over all the arguments (including the node path and the file path) using a loop:
You can get only the additional arguments by creating a new array that excludes the first 2
params:
34
Pass arguments from the command line
In this case:
args[0] is name=flavio , and you need to parse it. The best way to do so is by using the
35
Output to the command line
It is basically the same as the console object you find in the browser.
The most basic and most used method is console.log() , which prints the string you pass to it
to the console.
const x = 'x'
const y = 'y'
console.log(x, y)
We can also format pretty phrases by passing variables and a format specifier.
For example:
36
Output to the command line
Example:
console.log('%O', Number)
Counting elements
console.count() is a handy method.
const x = 1
const y = 2
const z = 3
console.count(
'The value of x is ' + x + ' and has been checked .. how many times?'
)
console.count(
'The value of x is ' + x + ' and has been checked .. how many times?'
)
console.count(
'The value of y is ' + y + ' and has been checked .. how many times?'
)
What happens is that count will count the number of times a string is printed, and print the
count next to it:
37
Output to the command line
This will print the stack trace. This is what's printed if I try this in the Node REPL:
Trace
at function2 (repl:1:33)
at function1 (repl:1:25)
at repl:1:1
at ContextifyScript.Script.runInThisContext (vm.js:44:33)
at REPLServer.defaultEval (repl.js:239:29)
at bound (domain.js:301:14)
at REPLServer.runBound [as eval] (domain.js:314:12)
at REPLServer.onLine (repl.js:440:10)
at emitOne (events.js:120:20)
at REPLServer.emit (events.js:210:7)
38
Output to the command line
It will not appear in the console, but it will appear in the error log.
Example:
console.log('\x1b[33m%s\x1b[0m', 'hi!')
You can try that in the Node REPL, and it will print hi! in yellow.
However, this is the low-level way to do this. The simplest way to go about coloring the
console output is by using a library. Chalk is such a library, and in addition to coloring it also
helps with other styling facilities, like making text bold, italic or underlined.
You install it with npm install chalk , then you can use it:
Using chalk.yellow is much more convenient than trying to remember the escape codes, and
the code is much more readable.
Check the project link I posted above for more usage examples.
This snippet creates a 10-step progress bar, and every 100ms one step is completed. When
the bar completes we clear the interval:
39
Output to the command line
40
Accept input from the command line
Node since version 7 provides the readline module to perform exactly this: get input from a
readable stream such as the process.stdin stream, which during the execution of a Node
program is the terminal input, one line at a time.
This piece of code asks the username, and once the text is entered and the user presses
enter, we send a greeting.
The question() method shows the first parameter (a question) and waits for the user input. It
calls the callback function once enter is pressed.
readline offers several other methods, and I'll let you check them out on the package
If you need to require a password, it's best to now echo it back, but instead showing a *
symbol.
The simplest way is to use the readline-sync package which is very similar in terms of the
API and handles this out of the box.
You can install it using npm install inquirer , and then you can replicate the above code like
this:
var questions = [{
type: 'input',
41
Accept input from the command line
name: 'name',
message: "What's your name?",
}]
inquirer.prompt(questions).then(answers => {
console.log(`Hi ${answers['name']}!`)
})
Inquirer.js lets you do many things like asking multiple choices, having radio buttons,
confirmations, and more.
It's worth knowing all the alternatives, especially the built-in ones provided by Node, but if you
plan to take CLI input to the next level, Inquirer.js is an optimal choice.
42
Expose functionality from a Node file using exports
to import the functionality exposed in the library.js file that resides in the current file folder.
In this file, functionality must be exposed before it can be imported by other files.
Any other object or variable defined in the file by default is private and not exposed to the
outer world.
This is what the module.exports API offered by the module system allows us to do.
When you assign an object or a function as a new exports property, that is the thing that's
being exposed, and as such, it can be imported in other parts of your app, or in other apps as
well.
The first is to assign an object to module.exports , which is an object provided out of the box
by the module system, and this will make your file export just that object:
const car = {
brand: 'Ford',
model: 'Fiesta'
}
module.exports = car
The second way is to add the exported object as a property of exports . This way allows you
to export multiple objects, functions or data:
43
Expose functionality from a Node file using exports
const car = {
brand: 'Ford',
model: 'Fiesta'
}
exports.car = car
or directly
exports.car = {
brand: 'Ford',
model: 'Fiesta'
}
And in the other file, you'll use it by referencing a property of your import:
or
The first exposes the object it points to. The latter exposes the properties of the object it points
to.
44
npm
npm
A quick guide to npm, the powerful package manager key to the success of
Node.js. In January 2017 over 350000 packages were reported being listed in
the npm registry, making it the biggest single language code repository on
Earth, and you can be sure there is a package for (almost!) everything.
Introduction to npm
Downloads
Installing all dependencies
Installing a single package
Updating packages
Versioning
Running Tasks
Introduction to npm
npm is the standard package manager for Node.js.
45
npm
In January 2017 over 350000 packages were reported being listed in the npm registry, making
it the biggest single language code repository on Earth, and you can be sure there is a
package for (almost!) everything.
It started as a way to download and manage dependencies of Node.js packages, but it has
since become a tool used also in frontend JavaScript.
Downloads
npm manages downloads of dependencies of your project.
npm install
it will install everything the project needs, in the node_modules folder, creating it if it's not
existing already.
--save installs and adds the entry to the package.json file dependencies
--save-dev installs and adds the entry to the package.json file devDependencies
The difference is mainly that devDependencies are usually development tools, like a testing
library, while dependencies are bundled with the app in production.
Updating packages
Updating is also made easy, by running
npm update
46
npm
npm will check all packages for a newer version that satisfies your versioning constraints.
Versioning
In addition to plain downloads, npm also manages versioning, so you can specify any
specific version of a package, or require a version higher or lower than what you need.
Many times you'll find that a library is only compatible with a major release of another library.
Specifying an explicit version of a library also helps to keep everyone on the same exact
version of a package, so that the whole team runs the same version until the package.json file
is updated.
In all those cases, versioning helps a lot, and npm follows the semantic versioning (semver)
standard.
Running Tasks
The package.json file supports a format for specifying command line tasks that can be run by
using
For example:
{
"scripts": {
"start-dev": "node lib/server-development",
"start": "node lib/server-production"
},
}
{
"scripts": {
47
npm
So instead of typing those long commands, which are easy to forget or mistype, you can run
48
Where does npm install the packages
Read the npm guide if you are starting out with npm, it's going to go in a lot of the basic
details of it.
When you install a package using npm (or yarn), you can perform 2 types of installation:
a local install
a global install
the package is installed in the current file tree, under the node_modules subfolder.
As this happens, npm also adds the lodash entry in the dependencies property of the
package.json file present in the current folder.
When this happens, npm won't install the package under the local folder, but instead, it will
use a global location.
Where, exactly?
The npm root -g command will tell you where that exact location is on your machine.
If you use nvm to manage Node.js versions, however, that location would differ.
49
How to use or execute a package installed using npm
When you install using npm a package into your node_modules folder, or also globally, how do
you use it in your Node code?
Say you install lodash , the popular JavaScript utility library, using
To use it in your code, you just need to import it into your program using require :
const _ = require('lodash)
In this case, it will put the executable file under the node_modules/.bin/ folder.
The cowsay package provides a command line program that can be executed to make a cow
say something (and other animals as well ).
When you install the package using npm install cowsay , it will install itself and a few
dependencies in the node_modules folder:
50
How to use or execute a package installed using npm
There is a hidden .bin folder, which contains symbolic links to the cowsay binaries:
You can of course type ./node_modules/.bin/cowsay to run it, and it works, but npx, included in
the recent versions of npm (since 5.2), is a much better option. You just run:
npx cowsay
51
How to use or execute a package installed using npm
52
The package.json file
If you work with JavaScript, or you've ever interacted with a JavaScript project, Node.js or a
frontend project, you surely met the package.json file.
What's that for? What should you know about it, and what are some of the cool things you can
do with it?
The package.json file is kind of a manifest for your project. It can do a lot of things, completely
unrelated. It's a central repository of configuration for tools, for example. It's also where npm
and yarn store the names and versions of the package it installed.
author
contributors
bugs
homepage
version
license
keywords
description
repository
main
private
scripts
dependencies
devDependencies
engines
browserslist
Command-specific properties
Package versions
53
The package.json file
It's empty! There are no fixed requirements of what should be in a package.json file, for an
application. The only requirement is that it respects the JSON format, otherwise it cannot be
read by programs that try to access its properties programmatically.
If you're building a Node.js package that you want to distribute over npm things change
radically, and you must have a set of properties that will help other people use it. We'll see
more about this later on.
{
"name": "test-project"
}
It defines a name property, which tells the name of the app, or package, that's contained in the
same folder where this file lives.
Here's a much more complex example, which I extracted this from a sample Vue.js
application:
{
"name": "test-project",
"version": "1.0.0",
"description": "A Vue.js project",
"main": "src/main.js",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"unit": "jest --config test/unit/jest.conf.js --coverage",
"test": "npm run unit",
"lint": "eslint --ext .js,.vue src test/unit",
"build": "node build/build.js"
},
"dependencies": {
"vue": "^2.5.2"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-eslint": "^8.2.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-jest": "^21.0.2",
"babel-loader": "^7.1.1",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-syntax-jsx": "^6.18.0",
54
The package.json file
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"eslint": "^4.15.0",
"eslint-config-airbnb-base": "^11.3.0",
"eslint-friendly-formatter": "^3.0.0",
"eslint-import-resolver-webpack": "^0.8.3",
"eslint-loader": "^1.7.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-vue": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"jest": "^22.0.4",
"jest-serializer-vue": "^0.3.0",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-jest": "^1.0.2",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
55
The package.json file
browserslist is used to tell which browsers (and their versions) you want to support
All those properties are used by either npm or other tools that we can use.
Properties breakdown
This section describes the properties you can use in detail. I refer to "package" but the same
thing applies to local applications which you do not use as packages.
Most of those properties are only used on the https://www.npmjs.com/, other by scripts that
interact with your code, like npm or others.
name
Sets the package name.
Example:
"name": "test-project"
The name must be less than 214 characters, must not have spaces, it can only contain
lowercase letters, hyphens ( - ) or underscores ( _ ).
This is because when a package is published on npm , it gets its own URL based on this
property.
If you published this package publicly on GitHub, a good value for this property is the GitHub
repository name.
author
Lists the package author name
Example:
56
The package.json file
{
"author": "Flavio Copes <[email protected]> (https://flaviocopes.com)"
}
{
"author": {
"name": "Flavio Copes",
"email": "[email protected]",
"url": "https://flaviocopes.com"
}
}
contributors
As well as the author, the project can have one or more contributors. This property is an array
that lists them.
Example:
{
"contributors": [
"Flavio Copes <[email protected]> (https://flaviocopes.com)"
]
}
{
"contributors": [
{
"name": "Flavio Copes",
"email": "[email protected]",
"url": "https://flaviocopes.com"
}
]
}
bugs
Links to the package issue tracker, most likely a GitHub issues page
Example:
{
"bugs": "https://github.com/flaviocopes/package/issues"
57
The package.json file
homepage
Sets the package homepage
Example:
{
"homepage": "https://flaviocopes.com/package"
}
version
Indicates the current version of the package.
Example:
"version": "1.0.0"
This property follows the semantic versioning (semver) notation for versions, which means the
version is always expressed with 3 numbers: x.x.x .
The first number is the major version, the second the minor version and the third is the patch
version.
There is a meaning in these numbers: a release that only fixes bugs is a patch release, a
release that introduces backward-compatible changes is a minor release, a major release can
have breaking changes.
license
Indicates the license of the package.
Example:
"license": "MIT"
keywords
This property contains an array of keywords that associate with what your package does.
Example:
58
The package.json file
"keywords": [
"email",
"machine learning",
"ai"
]
This helps people find your package when navigating similar packages, or when browsing the
https://www.npmjs.com/ website.
description
This property contains a brief description of the package
Example:
This is especially useful if you decide to publish your package to npm so that people can find
out what the package is about.
repository
This property specifies where this package repository is located.
Example:
"repository": "github:flaviocopes/testing",
Notice the github prefix. There are other popular services baked in:
"repository": "gitlab:flaviocopes/testing",
"repository": "bitbucket:flaviocopes/testing",
"repository": {
"type": "git",
"url": "https://github.com/flaviocopes/testing.git"
}
59
The package.json file
"repository": {
"type": "svn",
"url": "..."
}
main
Sets the entry point for the package.
When you import this package in an application, that's where the application will search for the
module exports.
Example:
"main": "src/main.js"
private
if set to true prevents the app/package to be accidentally published on npm
Example:
"private": true
scripts
Defines a set of node scripts you can run
Example:
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"unit": "jest --config test/unit/jest.conf.js --coverage",
"test": "npm run unit",
"lint": "eslint --ext .js,.vue src test/unit",
"build": "node build/build.js"
}
These scripts are command line applications. You can run them by calling npm run XXXX or
yarn XXXX , where XXXX is the command name. Example: npm run dev .
You can use any name you want for a command, and scripts can do literally anything you
want.
60
The package.json file
dependencies
Sets a list of npm packages installed as dependencies.
Example:
"dependencies": {
"vue": "^2.5.2"
}
devDependencies
Sets a list of npm packages installed as development dependencies.
They differ from dependencies because they are meant to be installed only on a development
machine, not needed to run the code in production.
Example:
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1"
}
engines
Sets which versions of Node.js and other commands this package/app work on
Example:
"engines": {
61
The package.json file
browserslist
Is used to tell which browsers (and their versions) you want to support. It's referenced by
Babel, Autoprefixer, and other tools, to only add the polyfills and fallbacks needed to the
browsers you target.
Example:
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
This configuration means you want to support the last 2 major versions of all browsers with at
least 1% of usage (from the CanIUse.com stats), except IE8 and lower.
(see more)
Command-specific properties
The package.json file can also host command-specific configuration, for example for Babel,
ESLint, and more.
Each has a specific property, like eslintConfig , babel and others. Those are command-
specific, and you can find how to use those in the respective command/project documentation.
Package versions
You have seen in the description above version numbers like these: ~3.0.0 or ^0.13.0 .
What do they mean, and which other version specifiers can you use?
That symbol specifies which updates you package accepts, from that dependency.
Given that using semver (semantic versioning) all versions have 3 digits, the first being the
major release, the second the minor release and the third is the patch release, you have these
rules:
~ : if you write ~0.13.0 , you want to only update patch releases: 0.13.1 is ok, but
62
The package.json file
0.14.0 is not.
^ : if you write ^0.13.0 , you want to update patch and minor releases: 0.13.1 , 0.14.0
and so on.
* : if you write * , that means you accept all updates, including major version upgrades.
> : you accept any version higher than the one you specify
>= : you accept any version equal to or higher than the one you specify
<= : you accept any version equal or lower to the one you specify
< : you accept any version lower to the one you specify
and you can combine most of the above in ranges, like this: 1.0.0 || >=1.1.0 <1.2.0 , to either
use 1.0.0 or one release from 1.1.0 up, but lower than 1.2.0.
63
The package-lock.json file
What's that? You probably know about the package.json file, which is much more common
and has been around for much longer.
The goal of the file is to keep track of the exact version of every package that is installed so
that a product is 100% reproducible in the same way even if packages are updated by their
maintainers.
This solves a very specific problem that package.json left unsolved. In package.json you can
set which versions you want to upgrade to (patch or minor), using the semver notation, for
example:
if you write ~0.13.0 , you want to only update patch releases: 0.13.1 is ok, but 0.14.0
is not.
if you write ^0.13.0 , you want to update patch and minor releases: 0.13.1 , 0.14.0 and
so on.
if you write 0.13.0 , that is the exact version that will be used, always
You don't commit to Git your node_modules folder, which is generally huge, and when you try
to replicate the project on another machine by using the npm install command, if you
specified the ~ syntax and a patch release of a package has been released, that one is going
to be installed. Same for ^ and minor releases.
If you specify exact versions, like 0.13.0 in the example, you are not affected by this
problem.
It could be you, or another person trying to initialize the project on the other side of the world
by running npm install .
So your original project and the newly initialized project are actually different. Even if a patch
or minor release should not introduce breaking changes, we all know bugs can (and so, they
will) slide in.
The package-lock.json sets your currently installed version of each package in stone, and
npm will use those exact versions when running npm install .
This concept is not new, and other programming languages package managers (like
Composer in PHP) use a similar system for years.
64
The package-lock.json file
The package-lock.json file needs to be committed to your Git repository, so it can be fetched
by other people, if the project is public or you have collaborators, or if you use Git as a source
for deployments.
The dependencies versions will be updated in the package-lock.json file when you run npm
update .
An example
This is an example structure of a package-lock.json file we get when we run npm install
cowsay in an empty folder:
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.
0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
},
"cowsay": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/cowsay/-/cowsay-1.3.1.tgz"
,
"integrity": "sha512-3PVFe6FePVtPj1HTeLin9v8WyLl+VmM1l1H/5P+BTTDkM
Ajufp+0F9eLjzRnOHzVAYeIYFF5po5NjRrgefnRMQ==",
"requires": {
"get-stdin": "^5.0.1",
"optimist": "~0.6.1",
"string-width": "~2.1.1",
"strip-eof": "^1.0.0"
}
},
"get-stdin": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.
1.tgz",
"integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g="
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/
is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"minimist": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10
.tgz",
65
The package-lock.json file
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
},
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"requires": {
"minimist": "~0.0.1",
"wordwrap": "~0.0.2"
}
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaT
jAqvVwdfeZ7w7aCvJD7ugkw==",
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "^3.0.0"
}
},
"strip-eof": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
},
"wordwrap": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
}
}
}
get-stdin
optimist
string-width
strip-eof
In turn, those packages require other packages, as we can see from the requires property
that some have:
66
The package-lock.json file
ansi-regex
is-fullwidth-code-point
minimist
wordwrap
strip-eof
They are added in alphabetical order into the file, and each one has a version field, a
resolved field that points to the package location, and an integrity string that we can use to
67
Find the installed version of an npm package
To see the latest version of all the npm package installed, including their dependencies:
npm list
Example:
❯ npm list
/Users/flavio/dev/node/cowsay
└─┬ [email protected]
├── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ └── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ └─┬ [email protected]
│ └── [email protected]
└── [email protected]
You can also just open the package-lock.json file, but this involves some visual scanning.
To get only your top-level packages (basically, the ones you told npm to install and you listed
in the package.json ), run npm list --depth=0 :
You can get the version of a specific package by specifying the name:
68
Find the installed version of an npm package
If you want to see what's the latest available version of the package on the npm repository, run
npm view [package_name] version :
1.3.1
69
How to install an older version of an npm package
You can install an old version of an npm package using the @ syntax:
Example:
You might also be interested in listing all the previous version of a package. You can do it with
npm view <package> versions :
[ '1.0.0',
'1.0.1',
'1.0.2',
'1.0.3',
'1.1.0',
'1.1.1',
'1.1.2',
'1.1.3',
'1.1.4',
'1.1.5',
'1.1.6',
'1.1.7',
'1.1.8',
'1.1.9',
'1.2.0',
'1.2.1',
70
How to install an older version of an npm package
'1.3.0',
'1.3.1' ]
71
How to update all the Node dependencies to their latest version
When you install a package using npm install <packagename> , the latest available version of
the package is downloaded and put in the node_modules folder, and a corresponding entry is
added to the package.json and package-lock.json files that are present in your current folder.
npm calculates the dependencies and installs the latest available version of those as well.
Let's say you install cowsay , a cool command line tool that lets you make a cow say things.
When you npm install cowsay , this entry is added to the package.json file:
{
"dependencies": {
"cowsay": "^1.3.1"
}
}
and this is an extract of package-lock.json , where I removed the nested dependencies for
clarity:
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"cowsay": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/cowsay/-/cowsay-1.3.1.tgz",
"integrity": "sha512-3PVFe6FePVtPj1HTeLin9v8WyLl+VmM1l1H/5P+BTTDkMAjufp+0F9eLjzRnOHz
VAYeIYFF5po5NjRrgefnRMQ==",
"requires": {
"get-stdin": "^5.0.1",
"optimist": "~0.6.1",
"string-width": "~2.1.1",
"strip-eof": "^1.0.0"
}
}
}
}
72
How to update all the Node dependencies to their latest version
Now those 2 files tell us that we installed version 1.3.1 of cowsay, and our rule for updates is
^1.3.1 , which for the npm versioning rules means that npm can update to patch and minor
If there is a new minor or patch release and we type npm update , the installed version is
updated, and the package-lock.json file diligently filled with the new version.
Here's the list of a few outdated packages in one repository I didn't update for quite a while:
Some of those updates are major releases. Running npm update won't update the version of
those. Major releases are never updated in this way because they (by definition) introduce
breaking changes, and npm want to save you trouble.
To update to a new major version all the packages, install the npm-check-updates package
globally:
ncu -u
73
How to update all the Node dependencies to their latest version
this will upgrade all the version hints in the package.json file, to dependencies and
devDependencies , so npm can install the new major version.
npm update
If you just downloaded the project without the node_modules dependencies and you want to
install the shiny new versions first, just run
npm install
74
Semantic versioning rules
If there's one great thing in Node.js packages, is that all agreed on using Semantic Versioning
for their version numbering.
The Semantic Versioning concept is simple: all versions have 3 digits: x.y.z .
When you make a new release, you don't just up a number as you please, but you have rules:
you up the major version when you make incompatible API changes
you up the minor version when you add functionality in a backward-compatible manner
you up the patch version when you make backward-compatible bug fixes
The convention is adopted all across programming languages, and it is very important that
every npm package adheres to it, because the whole system depends on that.
Because npm set some rules we can use in the package.json file to choose which versions it
can update our packages to, when we run npm update .
>
>=
<
<=
||
^ : if you write ^0.13.0 when running npm update it can update to patch and minor
75
Semantic versioning rules
> : you accept any version higher than the one you specify
>= : you accept any version equal to or higher than the one you specify
<= : you accept any version equal or lower to the one you specify
< : you accept any version lower to the one you specify
You can combine some of those notations, for example use 1.0.0 || >=1.1.0 <1.2.0 to either
use 1.0.0 or one release from 1.1.0 up, but lower than 1.2.0.
no symbol: you accept only that specific version you specify ( 1.2.1 )
latest : you want to use the latest version available
76
Uninstalling npm packages
To uninstall a package you have previously installed locally (using npm install <package-
name> in the node_modules folder, run
from the project root folder (the folder that contains the node_modules folder).
Using the -S flag, or --save , this operation will also remove the reference in the
package.json file.
If the package is installed globally, you need to add the -g / --global flag:
for example:
and you can run this command from anywhere you want on your system because the folder
where you currently are does not matter.
77
Global or local packages
local packages are installed in the directory where you run npm install <package-name> ,
and they are put in the node_modules folder under this directory
global packages are all put in a single place in your system (exactly where depends on
your setup), regardless of where you run npm install -g <package-name>
require('package-name')
This makes sure you can have dozens of applications in your computer, all running a different
version of each package if needed.
Updating a global package would make all your projects use the new release, and as you can
imagine this might cause nightmares in terms of maintenance, as some packages might break
compatibility with further dependencies, and so on.
All projects have their own local version of a package, even if this might appear like a waste of
resources, it's minimal compared to the possible negative consequences.
A package should be installed globally when it provides an executable command that you
run from the shell (CLI), and it's reused across projects.
You can also install executable commands locally and run them using npx, but some
packages are just better installed globally.
Great examples of popular global packages which you might know are
npm
create-react-app
vue-cli
grunt-cli
mocha
react-native-cli
gatsby-cli
78
Global or local packages
forever
nodemon
You probably have some packages installed globally already on your system. You can see
them by running
79
npm dependencies and devDependencies
When you install an npm package using npm install <package-name> , you are installing it as a
dependency.
The package is automatically listed in the package.json file, under the dependencies list (as of
npm 5: before you had to manually specify --save ).
When you add the -D flag, or --save-dev , you are installing it as a development
dependency, which adds it to the devDependencies list.
When you go in production, if you type npm install and the folder contains a package.json
file, they are installed, as npm assumes this is a development deploy.
You need to set the --production flag ( npm install --production ) to avoid installing those
development dependencies.
80
npx
npx
npx is a very cool way to run Node code, and provides many useful features
In this post, I want to introduce a very powerful command that's been available in npm starting
version 5.2, released in July 2017: npx.
If you don't want to install npm, you can install npx as a standalone package
npx lets you run code built with Node and published through the npm registry.
This was a pain because you could not really install different versions of the same command.
Running npx commandname automatically finds the correct reference of the command inside the
node_modules folder of a project, without needing to know the exact path, and without requiring
1) you don't need to install anything 2) you can run different versions of the same command,
using the syntax @version
A typical demonstration of using npx is through the cowsay command. cowsay will print a
cow saying what you wrote in the command. For example:
_______
< Hello >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
81
npx
|| ||
Now, this if you have the cowsay command globally installed from npm previously, otherwise
you'll get an error when you try to run the command.
npx allows you to run that npm command without having it installed locally:
running the vue CLI tool to create new applications and run them: npx vue create my-
vue-app
This helps to avoid tools like nvm or the other Node version management tools.
You can run code that sits in a GitHub gist, for example:
npx https://gist.github.com/zkat/4bc19503fe9e9309e2bfaa2c58074d32
Of course, you need to be careful when running code that you do not control, as with great
power comes great responsibility.
82
npx
83
The event loop
Introduction
Blocking the event loop
The call stack
A simple event loop explanation
Queuing function execution
The Message Queue
ES6 Job Queue
Introduction
The Event Loop is one of the most important aspects to understand about JavaScript.
I've programmed for years with JavaScript, yet I've never fully understood how things
work under the hoods. It's completely fine to not know this concept in detail, but as usual,
it's helpful to know how it works, and also you might just be a little curious at this point.
This post aims to explain the inner details of how JavaScript works with a single thread, and
how it handles asynchronous functions.
Your JavaScript code runs single threaded. There is just one thing happening at a time.
This is a limitation that's actually very helpful, as it simplifies a lot how you program without
worrying about concurrency issues.
You just need to pay attention to how you write your code and avoid anything that could block
the thread, like synchronous network calls or infinite loops.
In general, in most browsers there is an event loop for every browser tab, to make every
process isolated and avoid a web page with infinite loops or heavy processing to block your
entire browser.
The environment manages multiple concurrent event loops, to handle API calls for example.
Web Workers run in their own event loop as well.
You mainly need to be concerned that your code will run on a single event loop, and write
code with this thing in mind to avoid blocking it.
84
The event loop
Almost all the I/O primitives in JavaScript are non-blocking. Network requests, Node.js
filesystem operations, and so on. Being blocking is the exception, and this is why JavaScript is
based so much on callbacks, and more recently on promises and async/await.
The event loop continuously checks the call stack to see if there's any function that needs to
run.
While doing so, it adds any function call it finds to the call stack and executes each one in
order.
You know the error stack trace you might be familiar with, in the debugger or in the browser
console? The browser looks up the function names in the call stack to inform you which
function originates the current call:
85
The event loop
foo()
86
The event loop
foo
bar
baz
as expected.
When this code runs, first foo() is called. Inside foo() we first call bar() , then we call
baz() .
The event loop on every iteration looks if there's something in the call stack, and executes it:
87
The event loop
88
The event loop
The use case of setTimeout(() => {}), 0) is to call a function, but execute it once every other
function in the code has executed.
foo()
foo
baz
bar
When this code runs, first foo() is called. Inside foo() we first call setTimeout, passing bar as
an argument, and we instruct it to run immediately as fast as it can, passing 0 as the timer.
Then we call baz().
89
The event loop
Here is the execution order for all the functions in our program:
90
The event loop
91
The event loop
The Message Queue is also where user-initiated events like click or keyboard events, or fetch
responses are queued before your code has the opportunity to react to them. Or also DOM
events like onLoad .
The loop gives priority to the call stack, and it first processes everything it finds in the
call stack, and once there's nothing in there, it goes to pick up things in the event
queue.
We don't have to wait for functions like setTimeout , fetch or other things to do their own work,
because they are provided by the browser, and they live on their own threads. For example, if
you set the setTimeout timeout to 2 seconds, you don't have to wait 2 seconds - the wait
happens elsewhere.
Promises that resolve before the current function ends will be executed right after the current
function.
I find nice the analogy of a rollercoaster ride at an amusement park: the message queue puts
you back in queue with after all the other people in the queue, while the job queue is the
fastpass ticket that lets you take another ride right after you finished the previous one.
Example:
foo()
This prints
foo
92
The event loop
baz
should be right after baz, before bar
bar
That's a big difference between Promises (and Async/await, which is built on promises) and
plain old asynchronous functions through setTimeout() or other platform APIs.
93
nextTick
nextTick
The Node.js process.nextTick function interacts with the event loop in a
special way
As you try to understand the Node.js event loop, one important part of it is
process.nextTick() .
Every time the event loop takes a full trip, we call it a tick.
When we pass a function to process.nextTick() , we instruct the engine to invoke this function
at the end of the current operation, before the next event loop tick starts:
process.nextTick(() => {
//do something
})
When this operation ends, the JS engine runs all the functions passed to nextTick calls
during that operation.
It's the way we can tell the JS engine to process a function asynchronously (after the current
function), but as soon as possible, not queue it.
Calling setTimeout(() => {}, 0) will execute the function in the next tick, much later than
when using nextTick() .
Use nextTick() when you want to make sure that in the next event loop iteration that code is
already executed.
94
setImmediate
setImmediate
The Node.js setImmediate function interacts with the event loop in a special
way
When you want to execute some piece of code asynchronously, but as soon as possible, one
option is to use the setImmediate() function provided by Node.js:
setImmediate(() => {
//run something
})
Any function passed as the setImmediate() argument is a callback that's executed in the next
iteration of the event loop.
How is setImmediate() different from setTimeout(() => {}, 0) (passing a 0ms timeout), and
from process.nextTick() ?
A setTimeout() callback with a 0ms delay is very similar to setImmediate() . The execution
order will depend on various factors, but they will be both run in the next iteration of the event
loop.
95
Timers
Timers
When writing JavaScript code, you might want to delay the execution of a
function. Learn how to use setTimeout and setInterval to schedule functions
in the future
setTimeout()
Zero delay
setInterval()
Recursive setTimeout
setTimeout()
When writing JavaScript code, you might want to delay the execution of a function.
This is the job of setTimeout . You specify a callback function to execute later, and a value
expressing how later you want it to run, in milliseconds:
setTimeout(() => {
// runs after 2 seconds
}, 2000)
setTimeout(() => {
// runs after 50 milliseconds
}, 50)
This syntax defines a new function. You can call whatever other function you want in there, or
you can pass an existing function name, and a set of parameters:
96
Timers
setTimeout returns the timer id. This is generally not used, but you can store this id, and clear
// I changed my mind
clearTimeout(id)
Zero delay
If you specify the timeout delay to 0 , the callback function will be executed as soon as
possible, but after the current function execution:
setTimeout(() => {
console.log('after ')
}, 0)
This is especially useful to avoid blocking the CPU on intensive tasks and let other functions
be executed while performing a heavy calculation, by queuing functions in the scheduler.
Some browsers (IE and Edge) implement a setImmediate() method that does this same
exact functionality, but it's not standard and unavailable on other browsers. But it's a
standard function in Node.js.
setInterval()
callback function once, it will run it forever, at the specific time interval you specify (in
milliseconds):
setInterval(() => {
// runs every 2 seconds
}, 2000)
97
Timers
The function above runs every 2 seconds unless you tell it to stop, using clearInterval ,
passing it the interval id that setInterval returned:
clearInterval(id)
It's common to call clearInterval inside the setInterval callback function, to let it auto-
determine if it should run again or stop. For example this code runs something unless
App.somethingIWait has the value arrived :
Recursive setTimeout
setInterval starts a function every n milliseconds, without any consideration about when a
If a function takes always the same amount of time, it's all fine:
Maybe the function takes different execution times, depending on network conditions for
example:
98
Timers
To avoid this, you can schedule a recursive setTimeout to be called when the callback function
finishes:
setTimeout(myFunction, 1000)
}
setTimeout(
myFunction()
}, 1000)
setTimeout and setInterval are available in Node.js, through the Timers module.
Node.js also provides setImmediate() , which is equivalent to using setTimeout(() => {}, 0) ,
mostly used to work with the Node.js Event Loop.
99
Callbacks
Callbacks
JavaScript is synchronous by default, and is single threaded. This means
that code cannot create new threads and run in parallel. Find out what
asynchronous code means and how it looks like
Asynchronous means that things can happen independently of the main program flow.
100
Callbacks
In the current consumer computers, every program runs for a specific time slot, and then it
stops its execution to let another program continue its execution. This thing runs in a cycle so
fast that's impossible to notice, and we think our computers run many programs
simultaneously, but this is an illusion (except on multiprocessor machines).
Programs internally use interrupts, a signal that's emitted to the processor to gain the attention
of the system.
I won't go into the internals of this, but just keep in mind that it's normal for programs to be
asynchronous, and halt their execution until they need attention, and the computer can
execute other things in the meantime. When a program is waiting for a response from the
network, it cannot halt the processor until the request finishes.
Normally, programming languages are synchronous, and some provide a way to manage
asynchronicity, in the language or through libraries. C, Java, C#, PHP, Go, Ruby, Swift,
Python, they are all synchronous by default. Some of them handle async by using threads,
spawning a new process.
JavaScript
JavaScript is synchronous by default and is single threaded. This means that code cannot
create new threads and run in parallel.
Lines of code are executed in series, one after another, for example:
const a = 1
const b = 2
const c = a * b
console.log(c)
doSomething()
But JavaScript was born inside the browser, its main job, in the beginning, was to respond to
user actions, like onClick , onMouseOver , onChange , onSubmit and so on. How could it do this
with a synchronous programming model?
The answer was in its environment. The browser provides a way to do it by providing a set of
APIs that can handle this kind of functionality.
More recently, Node.js introduced a non-blocking I/O environment to extend this concept to file
access, network calls and so on.
Callbacks
101
Callbacks
You can't know when a user is going to click a button, so what you do is, you define an event
handler for the click event. This event handler accepts a function, which will be called when
the event is triggered:
document.getElementById('button').addEventListener('click', () => {
//item clicked
})
A callback is a simple function that's passed as a value to another function, and will only be
executed when the event happens. We can do this because JavaScript has first-class
functions, which can be assigned to variables and passed around to other functions (called
higher-order functions)
It's common to wrap all your client code in a load event listener on the window object, which
runs the callback function only when the page is ready:
window.addEventListener('load', () => {
//window loaded
//do what you want
})
setTimeout(() => {
// runs after 2 seconds
}, 2000)
XHR requests also accept a callback, in this example by assigning a function to a property that
will be called when a particular event occurs (in this case, the state of the request changes):
102
Callbacks
How do you handle errors with callbacks? One very common strategy is to use what Node.js
adopted: the first parameter in any callback function is the error object: error-first callbacks
If there is no error, the object is null . If there is an error, it contains some description of the
error and other information.
However every callback adds a level of nesting, and when you have lots of callbacks, the code
starts to be complicated very quickly:
window.addEventListener('load', () => {
document.getElementById('button').addEventListener('click', () => {
setTimeout(() => {
items.forEach(item => {
//your code here
})
}, 2000)
})
})
This is just a simple 4-levels code, but I've seen much more levels of nesting and it's not fun.
Alternatives to callbacks
Starting with ES6, JavaScript introduced several features that help us with asynchronous code
that do not involve using callbacks:
Promises (ES6)
Async/Await (ES8)
103
Callbacks
104
Promises
Promises
Promises are one way to deal with asynchronous code in JavaScript, without
writing too many callbacks in your code.
Introduction to promises
How promises work, in brief
Which JS API use promises?
Creating a promise
Consuming a promise
Chaining promises
Example of chaining promises
Handling errors
Cascading errors
Orchestrating promises
Promise.all()
Promise.race()
Common errors
Uncaught TypeError: undefined is not a promise
Introduction to promises
A promise is commonly defined as a proxy for a value that will eventually become
available.
Promises are one way to deal with asynchronous code, without writing too many callbacks in
your code.
Although being around since years, they have been standardized and introduced in ES2015,
and now they have been superseded in ES2017 by async functions.
Async functions use the promises API as their building block, so understanding them is
fundamental even if in newer code you'll likely use async functions instead of promises.
105
Promises
At this point, the caller function waits for it to either return the promise in a resolved state, or
in a rejected state, but as you know JavaScript is asynchronous, so the function continues its
execution while the promise does it work.
It's unlikely that in modern JavaScript you'll find yourself not using promises, so let's start
diving right into them.
Creating a promise
The Promise API exposes a Promise constructor, which you initialize using new Promise() :
As you can see the promise checks the done global constant, and if that's true, we return a
resolved promise, otherwise a rejected promise.
Using resolve and reject we can communicate back a value, in the above case we just
return a string, but it could be an object as well.
Consuming a promise
106
Promises
Running checkIfItsDone() will execute the isItDoneYet() promise and will wait for it to
resolve, using the then callback, and if there is an error, it will handle it in the catch
callback.
Chaining promises
A promise can be returned to another promise, creating a chain of promises.
A great example of chaining promises is given by the Fetch API, a layer on top of the
XMLHttpRequest API, which we can use to get a resource and queue a chain of promises to
execute when the resource is fetched.
The Fetch API is a promise-based mechanism, and calling fetch() is equivalent to defining
our own promise using new Promise() .
fetch('/todos.json')
.then(status)
107
Promises
.then(json)
.then((data) => { console.log('Request succeeded with JSON response', data) })
.catch((error) => { console.log('Request failed', error) })
In this example, we call fetch() to get a list of TODO items from the todos.json file found in
the domain root, and we create a chain of promises.
Running fetch() returns a response, which has many properties, and within those we
reference:
response also has a json() method, which returns a promise that will resolve with the
So given those premises, this is what happens: the first promise in the chain is a function that
we defined, called status() , that checks the response status and if it's not a success
response (between 200 and 299), it rejects the promise.
This operation will cause the promise chain to skip all the chained promises listed and will skip
directly to the catch() statement at the bottom, logging the Request failed text along with
the error message.
If that succeeds instead, it calls the json() function we defined. Since the previous promise,
when successful, returned the response object, we get it as an input to the second promise.
In this case, we return the data JSON processed, so the third promise receives the JSON
directly:
.then((data) => {
console.log('Request succeeded with JSON response', data)
})
Handling errors
In the example, in the previous section, we had a catch that was appended to the chain of
promises.
When anything in the chain of promises fails and raises an error or rejects the promise, the
control goes to the nearest catch() statement down the chain.
108
Promises
// or
Cascading errors
If inside the catch() you raise an error, you can append a second catch() to handle it, and
so on.
Orchestrating promises
Promise.all()
If you need to synchronize different promises, Promise.all() helps you define a list of
promises, and execute something when they are all resolved.
Example:
const f1 = fetch('/something.json')
const f2 = fetch('/something2.json')
109
Promises
You are not limited to using fetch of course, any promise is good to go.
Promise.race()
Promise.race() runs when the first of the promises you pass to it resolves, and it runs the
attached callback just once, with the result of the first promise resolved.
Example:
Common errors
Uncaught TypeError: undefined is not a promise
If you get the Uncaught TypeError: undefined is not a promise error in the console, make sure
you use new Promise() instead of just Promise()
110
async/await
async/await
Discover the modern approach to asynchronous functions in JavaScript.
JavaScript evolved in a very short time from callbacks to Promises, and
since ES2017 asynchronous JavaScript is even simpler with the async/await
syntax
Introduction
Why were async/await introduced?
How it works
A quick example
Promise all the things
The code is much simpler to read
Multiple async functions in series
Easier debugging
Introduction
JavaScript evolved in a very short time from callbacks to promises (ES2015), and since
ES2017 asynchronous JavaScript is even simpler with the async/await syntax.
Async functions are a combination of promises and generators, and basically, they are a
higher level abstraction over promises. Let me repeat: async/await is built on promises.
When Promises were introduced in ES2015, they were meant to solve a problem with
asynchronous code, and they did, but over the 2 years that separated ES2015 and ES2017, it
was clear that promises could not be the final solution.
Promises were introduced to solve the famous callback hell problem, but they introduced
complexity on their own, and syntax complexity.
They were good primitives around which a better syntax could be exposed to the developers,
so when the time was right we got async functions.
They make the code look like it's synchronous, but it's asynchronous and non-blocking behind
the scenes.
111
async/await
How it works
An async function returns a promise, like in this example:
When you want to call this function you prepend await , and the calling code will stop until
the promise is resolved or rejected. One caveat: the client function must be defined as
async . Here's an example:
A quick example
This is a simple example of async/await used to run a function asynchronously:
console.log('Before')
doSomething()
console.log('After')
The above code will print the following to the browser console:
Before
After
I did something //after 3s
112
async/await
Prepending the async keyword to any function means that the function will return a promise.
Even if it's not doing so explicitly, it will internally make it return a promise.
And this is a very simple example, the major benefits will arise when the code is much more
complex.
For example here's how you would get a JSON resource, and parse it, using promises:
getFirstUserData()
113
async/await
getFirstUserData()
watchOverSomeoneWatchingSomeoneDoingSomething().then((res) => {
console.log(res)
})
Will print:
Easier debugging
Debugging promises is hard because the debugger will not step over asynchronous code.
Async/await makes this very easy because to the compiler it's just like synchronous code.
114
The Node Event Emitter
If you worked with JavaScript in the browser, you know how much of the interaction of the user
is handled through events: mouse clicks, keyboard button presses, reacting to mouse
movements, and so on.
On the backend side, Node offers us the option to build a similar system using the events
module.
This module, in particular, offers the EventEmitter class, which we'll use to handle our events.
This object exposes, among many others, the on and emit methods.
on is used to add a callback function that's going to be executed when the event is
triggered
For example, let's create a start event, and as a matter of providing a sample, we react to
that by just logging to the console:
eventEmitter.on('start', () => {
console.log('started')
})
When we run
eventEmitter.emit('start')
the event handler function is triggered, and we get the console log.
You can pass arguments to the event handler by passing them as additional arguments to
emit() :
eventEmitter.emit('start', 23)
115
The Node Event Emitter
Multiple arguments:
eventEmitter.emit('start', 1, 100)
The EventEmitter object also exposes several other methods to interact with events, like
You can read all their details on the events module page at https://nodejs.org/api/events.html
116
HTTP
HTTP
A detailed description of how the HTTP protocol, and the Web, work
HTTP (Hyper Text Transfer Protocol) is one of the application protocols of TCP/IP, the suite of
protocols that powers the Internet.
Let me fix that: it's not one of the protocols, it's the most successful and popular one, by all
means.
HTTP is what makes the World Wide Web work, giving browsers a language to communicate
to remote servers that host web pages.
HTTP was first standardized in 1991, as a result of the work that Tim Berners-Lee did at
CERN, the European Center of Nuclear Research, since 1989.
The goal was to allow researchers to easily exchange and interlink their papers. It was meant
as a way for the scientific community to work better.
Back then the internet main applications basically consisted in FTP (the File Transfer
Protocol), Email and Usenet (newsgroups, today almost abandoned).
In 1993 Mosaic, the first graphical web browser, was released, and things skyrocketed from
there.
Over time the Web and the ecosystem around it have dramatically evolved, but the basics still
remain. One example of evolution: HTTP now powers, in addition to web pages, REST APIs,
one common way to programmatically access a service over the Internet.
HTTP got a minor revision in 1997 with HTTP/1.1, and in 2015 its successor, HTTP/2, was
standardized and it's now being implemented by the major Web Servers used across the
globe.
The HTTP protocol is considered insecure, just like any other protocol (SMTP, FTP..) not
served over an encrypted connection. This is why there is a big push nowadays towards using
HTTPS, which is HTTP served over TLS.
That said, the building blocks of HTTP/2 and HTTPS have their roots in HTTP, and in this
article I'll introduce how HTTP works.
HTML documents
117
HTTP
HTTP is the way web browsers like Chrome, Firefox, Edge and many others (also called
clients from here on) communicate with web servers.
The name Hyper Text Transfer Protocol derives from the need of transferring not just files, like
in FTP - the "File Transfer Protocol", but hypertexts, which would be written using HTML, and
then represented graphically by the browser with a nice presentation and interactive links.
Links were the driving force that drove adoption, along with the ease of creation of new web
pages.
HTTP is what transfer those hypertext files (and as we'll see also images and other file types)
over the network.
Hyperlinks
Inside a web browser, a document can point to another document using links.
A link is composed by a first part that determines the protocol and the server address, either
through a domain name or an IP.
Then there's the document part. Anything appended to the address part represents the
document path.
The path can be nested: https://flaviocopes.com/page/privacy/ and in this case the document
URL is /page/privacy .
The web server is responsible for interpreting the request and, once analyzed, serving the
correct response.
A request
What's in a request?
The first thing is the URL, which we've already seen before.
When we enter an address and press enter in our browser, under the hoods the server sends
to the correct IP address a request like this:
118
HTTP
GET /a-page
GET
POST
HEAD
PUT
DELETE
OPTIONS
TRACE
Headers are a set of key: value pairs that are used to communicate to the server-specific
information that is predefined, so the server can know what we mean.
Give that list a quick look. All of those headers are optional, except Host .
HTTP methods
GET
GET is the most used method here. It's the one that's used when you type an URL in the
browser address bar, or when you click a link.
HEAD
HEAD is just like GET, but tells the server to not send the response body back. Just the
headers.
119
HTTP
POST
The client uses the POST method to send data to the server. It's typically used in forms, for
example, but also when interacting with a REST API.
PUT
The PUT method is intended to create a resource at that specific URL, with the parameters
passed in the request body. Mainly used in REST APIs
DELETE
The DELETE method is called against an URL to request deletion of that resource. Mainly
used in REST APIs
OPTIONS
When a server receives an OPTIONS request it should send back the list of HTTP methods
allowed for that specific URL.
TRACE
Returns back to the client the request that has been received. Used for debugging or
diagnostic purposes.
Servers have no idea what's the current state of the client. All they care about is that they get
request and they need to fulfill them.
Any prior request is meaningless in this context, and this makes it possible for a web server to
be very fast, as there's less to process, and also it gives it bandwidth to handle a lot of
concurrent requests.
HTTP is also very lean, and communication is very fast in terms of overhead. This contrasts
with the protocols that were the most used at the time HTTP was introduced: TCP and
POP/SMTP, the mail protocols, which involve lots of handshaking and confirmations on the
receiving ends.
120
HTTP
Graphical browsers abstract all this communication, but we'll illustrate it here for learning
purposes.
A message is composed by a first line, which starts with the HTTP method, then contains the
resource relative path, and the protocol version:
After that, we need to add the HTTP request headers. As mentioned above, there are many
headers, but the only mandatory one is Host :
How can you test this? Using telnet. This is a command-line tool that lets us connect to any
server and send it commands.
Trying 178.128.202.129...
Connected to flaviocopes.com.
Escape character is '^]'.
You are connected to the Netlify web server that powers my blog. You can now type:
Redirecting to https://flaviocopes.com/axios/
121
HTTP
See, this is an HTTP response we got back from the server. It's a 301 Moved Permanently
request. See the HTTP status codes list to know more about the status codes.
Why? Because we connected to port 80, which is the default for HTTP, but on my server I set
up an automatic redirection to HTTPS.
There are other headers, all described in the HTTP response headers list.
In both the request and the response, an empty line separates the request header from the
request body. The request body in this case contains the string
Redirecting to https://flaviocopes.com/axios/
which is 46 bytes long, as specified in the Content-Length header. It is shown in the browser
when you open the page, while it automatically redirects you to the correct location.
In this case we're using telnet, the low-level tool that we can use to connect to any server, so
we can't have any kind of automatic redirect.
Let's do this process again, now connecting to port 443, which is the default port of the HTTPS
protocol. We can't use telnet because of the SSL handshake that must happen.
Let's keep things simple and use curl , another command-line tool. We cannot directly type
the HTTP request, but we'll see the response:
curl -i https://flaviocopes.com/axios/
HTTP/1.1 200 OK
Cache-Control: public, max-age=0, must-revalidate
Content-Type: text/html; charset=UTF-8
Date: Sun, 29 Jul 2018 14:20:45 GMT
Etag: "de3153d6eacef2299964de09db154b32-ssl"
Strict-Transport-Security: max-age=31536000
Age: 152
Content-Length: 9797
Connection: keep-alive
Server: Netlify
<!DOCTYPE html>
<html prefix="og: http://ogp.me/ns#" lang="en">
<head>
<meta charset="utf-8">
122
HTTP
I cut the response, but you can see that the HTML of the page is being returned now.
Other resources
An HTTP server will not just transfer HTML files, but typically it will also serve other files: CSS,
JS, SVG, PNG, JPG, lots of different file types.
HTTP is perfectly capable of transferring those files as well, and the client will know about the
file type, thus interpret them in the right way.
This is how the web works: when an HTML page is retrieved by the browser, it's interpreted
and any other resource it needs to display property (CSS, JavaScript, images..) is retrieved
through additional HTTP requests to the same server.
123
How HTTP Requests work
This article describes how browsers perform page requests using the HTTP/1.1 protocol
If you ever did an interview, you might have been asked: "what happens when you type
something into the Google search box and press enter".
It's one of the most popular questions you get asked. People just want to see if you can
explain some rather basic concepts and if you have any clue how the internet actually works.
In this post, I'll analyze what happens when you type an URL in the address bar of your
browser and press enter.
It's a very interesting topic to dissect in a blog post, as it touches many technologies I can dive
into in separate posts.
This is tech that is very rarely changed, and powers one the most complex and wide
ecosystems ever built by humans.
124
How HTTP Requests work
Modern browsers have the capability of knowing if the thing you wrote in the address bar is an
actual URL or a search term, and they will use the default search engine if it's not a valid URL.
When you enter the URL and press enter, the browser first builds the full URL.
If you just entered a domain, like flaviocopes.com , the browser by default will prepend
HTTP:// to it, defaulting to the HTTP protocol.
The domain name is a handy shortcut for us humans, but the internet is organized in such a
way that computers can look up the exact location of a server through its IP address, which is
a set of numbers like 222.324.3.1 (IPv4).
First, it checks the DNS local cache, to see if the domain has already been resolved recently.
Chrome has a handy DNS cache visualizer you can see at chrome://net-internals/#dns
If nothing is found there, the browser uses the DNS resolver, using the gethostbyname POSIX
system call to retrieve the host information.
gethostbyname
gethostbyname first looks in the local hosts file, which on macOS or Linux is located in
If this does not give any information about the domain, the system makes a request to the
DNS server.
Most people use the DNS server provided by their internet provider.
125
How HTTP Requests work
The browser performs the DNS request using the UDP protocol.
TCP and UDP are two of the foundational protocols of computer networking. They sit at the
same conceptual level, but TCP is connection-oriented, while UDP is a connectionless
protocol, more lightweight, used to send messages with little overhead.
How the UDP request is performed is not in the scope of this tutorial
The DNS server might have the domain IP in the cache. It not, it will ask the root DNS server.
That's a system (composed of 13 actual servers, distributed across the planet) that drives the
entire internet.
The DNS server does not know the address of each and every domain name on the planet.
A top-level domain is the domain extension: .com , .it , .pizza and so on.
Once the root DNS server receives the request, it forwards the request to that top-level
domain (TLD) DNS server.
Say you are looking for flaviocopes.com . The root domain DNS server returns the IP of the
.com TLD server.
Now our DNS resolver will cache the IP of that TLD server, so it does not have to ask the root
DNS server again for it.
The TLD DNS server will have the IP addresses of the authoritative Name Servers for the
domain we are looking for.
How? When you buy a domain, the domain registrar sends the appropriate TDL the
name servers. When you update the name servers (for example, when you change the
hosting provider), this information will be automatically updated by your domain registrar.
Those are the DNS servers of the hosting provider. They are usually more than 1, to serve as
backup.
For example:
ns1.dreamhost.com
ns2.dreamhost.com
ns3.dreamhost.com
The DNS resolver starts with the first, and tries to ask the IP of the domain (with the
subdomain, too) you are looking for.
126
How HTTP Requests work
A TCP connection requires a bit of handshaking before it can be fully initialized and you can
start sending data.
Example:
GET / HTTP/1.1
There are 2 mandatory fields, one of which is Host , and the other is Connection , while all the
other fields are optional:
Host: flaviocopes.com
Connection: close
127
How HTTP Requests work
Host indicates the domain name which we want to target, while Connection is always set to
Origin
Accept
Accept-Encoding
Cookie
Cache-Control
Dnt
Since we're now analyzing a GET request, the body is blank and we'll not look more into it.
The response
Once the request is sent, the server processes it and sends back a response.
The response starts with the status code and the status message. If the request is successful
and returns a 200, it will start with:
200 OK
The request might return a different status code and message, like one of these:
The response then contains a list of HTTP headers and the response body (which, since we're
making the request in the browser, is going to be HTML)
128
How HTTP Requests work
CSS files
images
the favicon
JavaScript files
...
How browsers render the page then is out of the scope, but it's important to understand that
the process I described is not just for the HTML pages, but for any item that's served over
HTTP.
129
Build an HTTP server
Here is the HTTP web server we used as the Node Hello World application in the Node.js
introduction
server.listen(port, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
The server is set to listen on the specified port, 3000 . When the server is ready, the listen
callback function is called.
The callback function we pass is the one that's going to be executed upon every request that
comes in. Whenever a new request is received, the request event is called, providing two
objects: a request (an http.IncomingMessage object) and a response (an http.ServerResponse
object).
request provides the request details. Through it, we access the request headers and request
data.
response is used to populate the data we're going to return to the client.
res.statusCode = 200
130
Build an HTTP server
res.setHeader('Content-Type', 'text/plain')
and we end close the response, adding the content as an argument to end() :
res.end('Hello World\n')
131
Making HTTP requests
I use the term HTTP, but HTTPS is what should be used everywhere, therefore these
examples use HTTPS instead of HTTP.
req.end()
const options = {
hostname: 'flaviocopes.com',
port: 443,
path: '/todos',
method: 'POST',
headers: {
132
Making HTTP requests
'Content-Type': 'application/json',
'Content-Length': data.length
}
}
req.write(data)
req.end()
133
Axios
Axios
Axios is a very convenient JavaScript library to perform HTTP requests in
Node.js
Introduction
Installation
The Axios API
GET requests
Add parameters to GET requests
POST Requests
Introduction
Axios is a very popular JavaScript library you can use to perform HTTP requests, that works in
both Browser and Node.js platforms.
It supports all modern browsers, including support for IE8 and higher.
It is promise-based, and this lets us write async/await code to perform XHR requests very
easily.
Using Axios has quite a few advantages over the native Fetch API:
134
Axios
Installation
Axios can be installed using npm:
or yarn:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
axios({
url: 'https://dog.ceo/api/breeds/list/all',
method: 'get',
data: {
foo: 'bar'
}
})
axios.get()
axios.post()
(like in jQuery you would use $.get() and $.post() instead of $.ajax() )
Axios offers methods for all the HTTP verbs, which are less popular but still used:
135
Axios
axios.delete()
axios.put()
axios.patch()
axios.options()
and a method to get the HTTP headers of a request, discarding the body:
axios.head()
GET requests
One convenient way to use Axios is to use the modern (ES2017) async/await syntax.
This Node.js example queries the Dog API to retrieve a list of all the dogs breeds, using
axios.get() , and it counts them:
if (breeds.data.message) {
console.log(`Got ${Object.entries(breeds.data.message).length} breeds`)
}
}
countBreeds()
If you don't want to use async/await you can use the Promises syntax:
136
Axios
.then(response => {
if (response.data.message) {
console.log(
`Got ${Object.entries(response.data.message).length} breeds`
)
}
})
.catch(error => {
console.log(error)
})
}
countBreeds()
With Axios you can perform this by simply using that URL:
axios.get('https://site.com/?foo=bar')
axios.get('https://site.com/', {
params: {
foo: 'bar'
}
})
POST Requests
Performing a POST request is just like doing a GET request, but instead of axios.get , you
use axios.post :
axios.post('https://site.com/')
axios.post('https://site.com/', {
foo: 'bar'
})
137
Axios
138
Websockets
Websockets
WebSockets are an alternative to HTTP communication in Web Applications.
They offer a long lived, bidirectional communication channel between client
and server.
They offer a long lived, bidirectional communication channel between client and server.
Once established, the channel is kept open, offering a very fast connection with low latency
and overhead.
139
Websockets
HTTP is a request/response protocol: the server returns some data when the client requests it.
With WebSockets:
the server can send a message to the client without the client explicitly requesting
something
the client and the server can talk to each other simultaneously
very little data overhead needs to be exchanged to send messages. This means a low
latency communication.
HTTP is great for occasional data exchange and interactions initiated by the client.
HTTP is much simpler to implement, while WebSockets require a bit more overhead.
Secured WebSockets
Always use the secure, encrypted protocol for WebSockets, wss:// .
ws:// refers to the unsafe WebSockets version (the http:// of WebSockets), and should be
Listen for it by assigning a callback function to the onopen property of the connection object:
connection.onopen = () => {
//...
}
140
Websockets
connection.onopen = () => {
connection.send('hey')
}
connection.onmessage = e => {
console.log(e.data)
}
We'll use it to build a WebSockets server. It can also be used to implement a client, and use
WebSockets to communicate between two backend services.
yarn init
yarn add ws
141
Websockets
wss.on('connection', ws => {
ws.on('message', message => {
console.log(`Received message => ${message}`)
})
ws.send('ho!')
})
This code creates a new server on port 8080 (the default port for WebSockets), and adds a
callback function when a connection is established, sending ho! to the client, and logging the
messages it receives.
142
HTTPS, secure connections
When you open your browser and ask a web server to send you a webpage, your data
performs 2 trips: 1 from the browser to the web server, and 1 from the web server to the
browser.
Then, depending on the content of the web page, you might have more connections required
to get the CSS files, the JavaScript files, images, and so on.
During any of those connections, any network your data is going to cross can be inspected
and manipulated.
The consequences can be serious: you might have all your network activity monitored and
logged, by a 3rd part you are not even aware it exist, some networks might inject ads, and you
might be subject to a man-in-the-middle attack, a security threat where the attacker can
manipulate your data and even impersonate your computer over the network. It's very easy for
someone to just listen to HTTP packets being transmitted over a public and unencrypted Wi-Fi
network.
HTTPS aims to solve the problem at the root: the entire communication between your browser
and the web server is encrypted.
Privacy and security are a major concern in today's internet. A few years ago, you could get
away with just using an encrypted connection in login-protected pages, or during an e-
commerce checkout. Also because of SSL certificates pricing and complications, most
websites just used HTTP.
Today HTTPS is a requirement on any site. More than 50% of the whole Web uses it now.
Google Chrome recently started marking HTTP sites as insecure, just to give you a valid
reason to have HTTPS mandatory (and forced) on all your websites.
When using HTTP the default server port is 80, and on HTTPS it's 443. It does not need to be
explicitly added if the server uses the default port, of course.
HTTPS is also sometimes called HTTP over SSL, or HTTP over TLS.
The difference between the two is simple: TLS is the successor of SSL.
When using HTTPS, the only thing that is not encrypted is the web server domain, and the
server port.
143
HTTPS, secure connections
Every other information, including the resource path, headers, cookies and query parameters
are all encrypted.
I won't go in the details of analyzing how the TLS protocol works under the hoods, but you
might think it's adding a good amount of overhead, and you would be right.
Any computation that's added to the processing of network resources causes overhead both
on the client, the server, and to the transmitted packets size.
However HTTPS enables the use of the newest protocol HTTP/2, which has a huge
advantage over HTTP/1.1: it way faster.
Why? There are many reasons, one is header compression, one is resource multiplexing. One
is server push: the server can push more resources when one resource is requested. So, if the
browser requests a page, it will also receive all the resources needed (images, CSS, JS).
Details aside, HTTP/2 is a huge improvement over HTTP/1.1 and it requires HTTPS. This
means that HTTPS, despite having the encryption overhead, happens to be way faster than
HTTP, if things are properly configured with a modern setup.
144
File descriptors
File descriptors
How to interact with file descriptors using Node
Before you're able to interact with a file that sits in your filesystem, you must get a file
descriptor.
A file descriptor is what's returned by opening the file using the open() method offered by the
fs module:
const fs = require('fs')
w+ open the file for reading and writing, positioning the stream at the beginning of the
if not existing
a+ open the file for reading and writing, positioning the stream at the end of the file. The
You can also open the file by using the fs.openSync method, which instead of providing the
file descriptor object in a callback, it returns it:
const fs = require('fs')
try {
const fd = fs.openSync('/Users/flavio/test.txt', 'r')
} catch (err) {
console.error(err)
}
Once you get the file descriptor, in whatever way you choose, you can perform all the
operations that require it, like calling fs.open() and many other operations that interact with
the filesystem.
145
File descriptors
146
File stats
File stats
How to get the details of a file using Node
Every file comes with a set of details that we can inspect using Node.
You call it passing a file path, and once Node gets the file details it will call the callback
function you pass, with 2 parameters: an error message, and the file stats:
const fs = require('fs')
fs.stat('/Users/flavio/test.txt', (err, stats) => {
if (err) {
console.error(err)
return
}
//we have access to the file stats in `stats`
})
Node provides also a sync method, which blocks the thread until the file stats are ready:
const fs = require('fs')
try {
const stats = fs.stat('/Users/flavio/test.txt')
} catch (err) {
console.error(err)
}
The file information is included in the stats variable. What kind of information can we extract
using the stats?
A lot, including:
There are other advanced methods, but the bulk of what you'll use in your day-to-day
programming is this.
const fs = require('fs')
fs.stat('/Users/flavio/test.txt', (err, stats) => {
if (err) {
console.error(err)
return
}
147
File stats
stats.isFile() //true
stats.isDirectory() //false
stats.isSymbolicLink() //false
stats.size //1024000 //= 1MB
})
148
File paths
File paths
How to interact with file paths and manipulate them in Node
/users/flavio/file.txt
while Windows computers are different, and have a structure such as:
C:\users\flavio\file.txt
You need to pay attention when using paths in your applications, as this difference must be
taken into account.
Example:
path.dirname(notes) // /users/flavio
path.basename(notes) // notes.txt
path.extname(notes) // .txt
You can get the file name without the extension by specifying a second argument to
basename :
149
File paths
You can get the absolute path calculation of a relative path using path.resolve() :
In this case Node will simply append /flavio.txt to the current working directory. If you
specify a second parameter folder, resolve will use the first as a base for the second:
If the first parameter starts with a slash, that means it's an absolute path:
path.resolve('/etc', 'flavio.txt')//'/etc/flavio.txt'
path.normalize() is another useful function, that will try and calculate the actual path, when it
path.normalize('/users/flavio/..//test.txt') ///users/test.txt
Both resolve and normalize will not check if the path exists. They just calculate a path
based on the information they got.
150
Reading files
Reading files
How to read files using Node
The simplest way to read a file in Node is to use the fs.readFile() method, passing it the file
path and a callback function that will be called with the file data (and the error):
const fs = require('fs')
const fs = require('fs')
try {
const data = fs.readFileSync('/Users/flavio/test.txt', 'utf8')
console.log(data)
} catch (err) {
console.error(err)
}
The default encoding is utf8, but you can specify a custom encoding using a a second
parameter.
Both fs.readFile() and fs.readFileSync() read the full content of the file in memory before
returning the data.
This means that big files are going to have a major impact on your memory consumption and
speed of execution of the program.
In this case, a better option is to read the file content using streams.
151
Writing files
Writing files
How to write files using Node
The easiest way to write to files in Node.js is to use the fs.writeFile() API.
Example:
const fs = require('fs')
const fs = require('fs')
try {
const data = fs.writeFileSync('/Users/flavio/test.txt', content)
//file written successfully
} catch (err) {
console.error(err)
}
By default, this API will replace the contents of the file if it does already exist.
w+ open the file for reading and writing, positioning the stream at the beginning of the
if not existing
152
Writing files
a+ open the file for reading and writing, positioning the stream at the end of the file. The
Append to a file
A handy method to append content to the end of a file is fs.appendFile() (and its
fs.appendFileSync() counterpart):
Using streams
All those methods write the full content to the file before returning the control back to your
program (in the async version, this means executing the callback)
In this case, a better option is to write the file content using streams.
153
Working with folders
The Node.js fs core module provides many handy methods you can use to work with
folders.
const fs = require('fs')
try {
if (!fs.existsSync(dir)){
fs.mkdirSync(dir)
}
} catch (err) {
console.error(err)
}
This piece of code reads the content of a folder, both files and subfolders, and returns their
relative path:
const fs = require('fs')
const path = require('path')
fs.readdirSync(folderPath)
154
Working with folders
fs.readdirSync(folderPath).map(fileName => {
return path.join(folderPath, fileName)
}
You can also filter the results to only return the files, and exclude the folders:
fs.readdirSync(folderPath).map(fileName => {
return path.join(folderPath, fileName)).filter(isFile)
}
Rename a folder
Use fs.rename() or fs.renameSync() to rename folder. The first parameter is the current path,
the second the new path:
const fs = require('fs')
const fs = require('fs')
try {
fs.renameSync('/Users/flavio', '/Users/roger')
} catch (err) {
console.error(err)
}
Remove a folder
Use fs.rmdir() or fs.rmdirSync() to remove a folder.
Removing a folder that has content can be more complicated than you need.
155
Working with folders
In this case I recommend installing the fs-extra module, which is very popular and well
maintained, and it's a drop-in replacement of the fs module, providing more features on top
of it.
Install it using
npm install fs-extra
const fs = require('fs-extra')
fs.remove(folder).then(() => {
//done
}).catch(err => {
console.error(err)
})
or with async/await:
156
The fs module
The fs module
The fs module of Node.js provides useful functions to interact with the file
system
The fs module provides a lot of very useful functionality to access and interact with the file
system.
There is no need to install it. Being part of the Node core, it can be used by simply requiring it:
const fs = require('fs')
Once you do so, you have access to all its methods, which include:
fs.access() : check if the file exists and Node can access it with its permissions
fs.appendFile() : append data to a file. If the file does not exist, it's created
fs.chmod() : change the permissions of a file specified by the filename passed. Related:
fs.lchmod() , fs.fchmod()
fs.chown() : change the owner and group of a file specified by the filename passed.
fs.stat() : returns the status of the file identified by the filename passed. Related:
fs.fstat() , fs.lstat()
fs.truncate() : truncate to the specified length the file identified by the filename passed.
Related: fs.ftruncate()
fs.unlink() : remove a file or a symbolic link
157
The fs module
fs.utimes() : change the timestamp of the file identified by the filename passed. Related:
fs.futimes()
One peculiar thing about the fs module is that all the methods are asynchronous by default,
but they can also work synchronously by appending Sync .
For example:
fs.rename()
fs.renameSync()
fs.write()
fs.writeSync()
For example let's examine the fs.rename() method. The asynchronous API is used with a
callback:
const fs = require('fs')
//done
})
A synchronous API can be used like this, with a try/catch block to handle errors:
const fs = require('fs')
try {
fs.renameSync('before.json', 'after.json')
//done
} catch (err) {
console.error(err)
}
The key difference here is that the execution of your script will block in the second example,
until the file operation succeeded.
158
The fs module
159
The path module
The path module provides a lot of very useful functionality to access and interact with the file
system.
There is no need to install it. Being part of the Node core, it can be used by simply requiring it:
This module provides path.sep which provides the path segment separator ( \ on Windows,
and / on Linux / macOS), and path.delimiter which provides the path delimiter ( ; on
Windows, and : on Linux / macOS).
path.basename()
path.dirname()
path.extname()
path.isAbsolute()
path.join()
path.normalize()
path.parse()
path.relative()
path.resolve()
path.basename()
Return the last portion of a path. A second parameter can filter out the file extension:
require('path').basename('/test/something') //something
require('path').basename('/test/something.txt') //something.txt
require('path').basename('/test/something.txt', '.txt') //something
path.dirname()
Return the directory part of a path:
require('path').dirname('/test/something') // /test
require('path').dirname('/test/something/file.txt') // /test/something
160
The path module
path.extname()
Return the extension part of a path
require('path').dirname('/test/something') // ''
require('path').dirname('/test/something/file.txt') // '.txt'
path.isAbsolute()
Returns true if it's an absolute path
require('path').isAbsolute('/test/something') // true
require('path').isAbsolute('./test/something') // false
path.join()
Joins two or more parts of a path:
path.normalize()
Tries to calculate the actual path when it contains relative specifiers like . or .. , or double
slashes:
require('path').normalize('/users/flavio/..//test.txt') ///users/test.txt
path.parse()
Parses a path to an object with the segments that compose it:
Example:
require('path').parse('/users/test.txt')
161
The path module
results in
{
root: '/',
dir: '/users',
base: 'test.txt',
ext: '.txt',
name: 'test'
}
path.relative()
Accepts 2 paths as arguments. Returns the the relative path from the first path to the second,
based on the current working directory.
Example:
path.resolve()
You can get the absolute path calculation of a relative path using path.resolve() :
By specifying a second parameter, resolve will use the first as a base for the second:
If the first parameter starts with a slash, that means it's an absolute path:
path.resolve('/etc', 'flavio.txt')//'/etc/flavio.txt'
162
The os module
The os module
The os module of Node.js provides useful functions to interact with
underlying system
This module provides many functions that you can use to retrieve information from the
underlying operating system and the computer the program runs on, and interact with it.
const os = require('os')
There are a few useful properties that tell us some key things related to handling files:
os.EOL gives the line delimiter sequence. It's \n on Linux and macOS, and \r\n on
Windows.
When I say Linux and macOS I mean POSIX platforms. For simplicity I exclude other less
popular operating systems Node can run on.
os.constants.signals tells us all the constants related to handling process signals, like
os.constants.errno sets the constants for error reporting, like EADDRINUSE, EOVERFLOW
and more.
os.arch()
os.cpus()
os.endianness()
os.freemem()
os.homedir()
os.hostname()
os.loadavg()
os.networkInterfaces()
os.platform()
os.release()
os.tmpdir()
os.totalmem()
os.type()
os.uptime()
os.userInfo()
163
The os module
os.arch()
Return the string that identifies the underlying architecture, like arm , x64 , arm64 .
os.cpus()
Example:
os.endianness()
Return BE or LE depending if Node was compiled with Big Endian or Little Endian.
os.freemem()
Return the number of bytes that represent the free memory in the system.
os.homedir()
Example:
'/Users/flavio'
164
The os module
os.hostname()
os.loadavg()
Return the calculation made by the operating system on the load average.
Example:
os.networkInterfaces()
Example:
{ lo0:
[ { address: '127.0.0.1',
netmask: '255.0.0.0',
family: 'IPv4',
mac: 'fe:82:00:00:00:00',
internal: true },
{ address: '::1',
netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
family: 'IPv6',
mac: 'fe:82:00:00:00:00',
scopeid: 0,
internal: true },
{ address: 'fe80::1',
netmask: 'ffff:ffff:ffff:ffff::',
family: 'IPv6',
mac: 'fe:82:00:00:00:00',
scopeid: 1,
internal: true } ],
en1:
[ { address: 'fe82::9b:8282:d7e6:496e',
netmask: 'ffff:ffff:ffff:ffff::',
family: 'IPv6',
mac: '06:00:00:02:0e:00',
scopeid: 5,
internal: false },
{ address: '192.168.1.38',
netmask: '255.255.255.0',
family: 'IPv4',
mac: '06:00:00:02:0e:00',
165
The os module
internal: false } ],
utun0:
[ { address: 'fe80::2513:72bc:f405:61d0',
netmask: 'ffff:ffff:ffff:ffff::',
family: 'IPv6',
mac: 'fe:80:00:20:00:00',
scopeid: 8,
internal: false } ] }
os.platform()
darwin
freebsd
linux
openbsd
win32
...more
os.release()
os.tmpdir()
os.totalmem()
Returns the number of bytes that represent the total memory available in the system.
os.type()
Linux
Darwin on macOS
Windows_NT on Windows
os.uptime()
166
The os module
Returns the number of seconds the computer has been running since it was last rebooted.
os.userInfo()
167
The events module
The events module provides us the EventEmitter class, which is key to working with events in
Node.
I published a full article on that, so here I will just describe the API without further examples on
how to use it.
The event listener eats its own dog food and uses these events:
emitter.addListener()
emitter.emit()
emitter.eventNames()
emitter.getMaxListeners()
emitter.listenerCount()
emitter.listeners()
emitter.off()
emitter.on()
emitter.once()
emitter.prependListener()
emitter.prependOnceListener()
emitter.removeAllListeners()
emitter.removeListener()
emitter.setMaxListeners()
emitter.addListener()
emitter.emit()
168
The events module
Emits an event. It synchronously calls every event listener in the order they were registered.
emitter.eventNames()
Return an array of strings that represent the events registered on the current EventListener:
door.eventNames()
emitter.getMaxListeners()
Get the maximum amount of listeners one can add to an EventListener object, which defaults
to 10 but can be increased or lowered by using setMaxListeners()
door.getMaxListeners()
emitter.listenerCount()
door.listenerCount('open')
emitter.listeners()
door.listeners('open')
emitter.off()
emitter.on()
Usage:
door.on('open', () => {
169
The events module
emitter.once()
Adds a callback function that's called when an event is emitted for the first time after
registering this. This callback is only going to be called once, never again.
ee.once('my-event', () => {
//call callback function once
})
emitter.prependListener()
When you add a listener using on or addListener , it's added last in the queue of listeners,
and called last. Using prependListener it's added, and called, before other listeners.
emitter.prependOnceListener()
When you add a listener using once , it's added last in the queue of listeners, and called last.
Using prependOnceListener it's added, and called, before other listeners.
emitter.removeAllListeners()
door.removeAllListeners('open')
emitter.removeListener()
Remove a specific listener. You can do this by saving the callback function to a variable, when
added, so you can reference it later:
170
The events module
emitter.setMaxListeners()
Sets the maximum amount of listeners one can add to an EventListener object, which defaults
to 10 but can be increased or lowered.
door.setMaxListeners(50)
171
The http module
Properties
http.METHODS
http.STATUS_CODES
http.globalAgent
Methods
http.createServer()
http.request()
http.get()
Classes
http.Agent
http.ClientRequest
http.Server
http.ServerResponse
http.IncomingMessage
The module provides some properties and methods, and some classes.
Properties
http.METHODS
This property lists all the HTTP methods supported:
> require('http').METHODS
[ 'ACL',
'BIND',
'CHECKOUT',
'CONNECT',
'COPY',
'DELETE',
'GET',
172
The http module
'HEAD',
'LINK',
'LOCK',
'M-SEARCH',
'MERGE',
'MKACTIVITY',
'MKCALENDAR',
'MKCOL',
'MOVE',
'NOTIFY',
'OPTIONS',
'PATCH',
'POST',
'PROPFIND',
'PROPPATCH',
'PURGE',
'PUT',
'REBIND',
'REPORT',
'SEARCH',
'SUBSCRIBE',
'TRACE',
'UNBIND',
'UNLINK',
'UNLOCK',
'UNSUBSCRIBE' ]
http.STATUS_CODES
This property lists all the HTTP status codes and their description:
> require('http').STATUS_CODES
{ '100': 'Continue',
'101': 'Switching Protocols',
'102': 'Processing',
'200': 'OK',
'201': 'Created',
'202': 'Accepted',
'203': 'Non-Authoritative Information',
'204': 'No Content',
'205': 'Reset Content',
'206': 'Partial Content',
'207': 'Multi-Status',
'208': 'Already Reported',
'226': 'IM Used',
'300': 'Multiple Choices',
'301': 'Moved Permanently',
'302': 'Found',
'303': 'See Other',
'304': 'Not Modified',
'305': 'Use Proxy',
'307': 'Temporary Redirect',
'308': 'Permanent Redirect',
'400': 'Bad Request',
173
The http module
'401': 'Unauthorized',
'402': 'Payment Required',
'403': 'Forbidden',
'404': 'Not Found',
'405': 'Method Not Allowed',
'406': 'Not Acceptable',
'407': 'Proxy Authentication Required',
'408': 'Request Timeout',
'409': 'Conflict',
'410': 'Gone',
'411': 'Length Required',
'412': 'Precondition Failed',
'413': 'Payload Too Large',
'414': 'URI Too Long',
'415': 'Unsupported Media Type',
'416': 'Range Not Satisfiable',
'417': 'Expectation Failed',
'418': 'I\'m a teapot',
'421': 'Misdirected Request',
'422': 'Unprocessable Entity',
'423': 'Locked',
'424': 'Failed Dependency',
'425': 'Unordered Collection',
'426': 'Upgrade Required',
'428': 'Precondition Required',
'429': 'Too Many Requests',
'431': 'Request Header Fields Too Large',
'451': 'Unavailable For Legal Reasons',
'500': 'Internal Server Error',
'501': 'Not Implemented',
'502': 'Bad Gateway',
'503': 'Service Unavailable',
'504': 'Gateway Timeout',
'505': 'HTTP Version Not Supported',
'506': 'Variant Also Negotiates',
'507': 'Insufficient Storage',
'508': 'Loop Detected',
'509': 'Bandwidth Limit Exceeded',
'510': 'Not Extended',
'511': 'Network Authentication Required' }
http.globalAgent
Points to the global instance of the Agent object, which is an instance of the http.Agent class.
It's used to manage connections persistance and reuse for HTTP clients, and it's a key
component of Node HTTP networking.
Methods
174
The http module
http.createServer()
Return a new instance of the http.Server class.
Usage:
http.request()
Makes an HTTP request to a server, creating an instance of the http.ClientRequest class.
http.get()
Similar to http.request() , but automatically sets the HTTP method to GET, and calls
req.end() automatically.
Classes
The HTTP module provides 5 classes:
http.Agent
http.ClientRequest
http.Server
http.ServerResponse
http.IncomingMessage
http.Agent
Node creates a global instance of the http.Agent class to manage connections persistance
and reuse for HTTP clients, a key component of Node HTTP networking.
This object makes sure that every request made to a server is queued and a single socket is
reused.
http.ClientRequest
An http.ClientRequest object is created when http.request() or http.get() is called.
175
The http module
When a response is received, the response event is called with the response, with an
http.IncomingMessage instance as argument.
http.Server
This class is commonly instantiated and returned when creating a new server using
http.createServer() .
Once you have a server object, you have access to its methods:
http.ServerResponse
Created by an http.Server and passed as the second parameter to the request event it
fires.
The method you'll always call in the handler is end() , which closes the response, the
message is complete and the server can send it to the client. It must be called on each
response.
getHeaderNames() get the list of the names of the HTTP headers already set
headersSent() return true if the headers have already been sent to the client
176
The http module
After processing the headers you can send them to the client by calling response.writeHead() ,
which accepts the statusCode as the first parameter, the optional status message, and the
headers object.
To send data to the client in the response body, you use write() . It will send buffered data to
the HTTP response stream.
If the headers were not sent yet using response.writeHead() , it will send the headers first, with
the status code and message that's set in the request, which you can edit by setting the
statusCode and statusMessage properties values:
response.statusCode = 500
response.statusMessage = 'Internal Server Error'
http.IncomingMessage
An http.IncomingMessage object is created by:
The data is accessed using streams, since http.IncomingMessage implements the Readable
Stream interface.
177
Streams
Streams
Learn what streams are for, why are they so important, and how to use them.
They are a way to handle reading/writing files, network communications, or any kind of end-to-
end information exchange in an efficient way.
Streams are not a concept unique to Node.js. They were introduced in the Unix operating
system decades ago, and programs can interact with each other passing streams through the
pipe operator ( | ).
For example, in the traditional way, when you tell the program to read a file, the file is read into
memory, from start to finish, and then you process it.
Using streams you read it piece by piece, processing its content without keeping it all in
memory.
The Node.js stream module provides the foundation upon which all streaming APIs are build.
Why streams
Streams basically provide two major advantages using other data handling methods:
Memory efficiency: you don't need to load large amounts of data in memory before you
178
Streams
An example of a stream
A typical example is the one of reading files from a disk.
Using the Node fs module you can read a file, and serve it over HTTP when a new
connection is established to your http server:
readFile() reads the full contents of the file, and invokes the callback function when it's done.
res.end(data) in the callback will return the file contents to the HTTP client.
If the file is big, the operation will take quite a bit of time. Here is the same thing written using
streams:
Instead of waiting until the file is fully read, we start streaming it to the HTTP client as soon as
we have a chunk of data ready to be sent.
pipe()
The above example uses the line stream.pipe(res) : the pipe() method is called on the file
stream.
179
Streams
What does this code do? It takes the source, and pipes it into a destination.
You call it on the source stream, so in this case, the file stream is piped to the HTTP response.
The return value of the pipe() method is the destination stream, which is a very convenient
thing that lets us chain multiple pipe() calls, like this:
src.pipe(dest1).pipe(dest2)
src.pipe(dest1)
dest1.pipe(dest2)
stream
zlib.createGzip() compress data using gzip (a compression algorithm) into a stream
stream
zlib.createInflate() decompress a deflate stream
Readable : a stream you can pipe from, but not pipe into (you can receive data, but not
send data to it). When you push data into a readable stream, it is buffered, until a
consumer starts to read the data.
Writable : a stream you can pipe into, but not pipe from (you can send data, but not
180
Streams
input
readableStream.push('hi!')
readableStream.push('ho!')
process.stdin.pipe(writableStream)
181
Streams
readableStream.pipe(writableStream)
readableStream.push('hi!')
readableStream.push('ho!')
You can also consume a readable stream directly, using the readable event:
readableStream.on('readable', () => {
console.log(readableStream.read())
})
writableStream.write('hey!\n')
readableStream.pipe(writableStream)
182
Streams
readableStream.push('hi!')
readableStream.push('ho!')
writableStream.end()
Conclusion
This is an introduction to streams. There are much more complicated aspects to analyze, and I
hope to cover them soon.
183
Working with MySQL
The Node ecosystem of course has several different packages that allow you to interface with
MySQL, store data, retrieve data, and so on.
We'll use mysqljs/mysql , a package that has over 12.000 GitHub stars and has been around
for years.
const options = {
user: 'the_mysql_user_name',
password: 'the_mysql_user_password',
database: 'the_mysql_database_name'
}
const connection = mysql.createConnection(options)
connection.connect(err => {
if (err) {
console.error('An error occurred while connecting to the DB')
throw err
}
})
184
Working with MySQL
const options = {
user: 'the_mysql_user_name',
password: 'the_mysql_user_password',
database: 'the_mysql_database_name'
}
ssl , used to setup an SSL connection to the server (out of the scope of this tutorial)
const id = 223
connection.query('SELECT * FROM todos WHERE id = ?', [id], (error, todos, fields) => {
if (error) {
console.error('An error occurred while executing the query')
throw error
}
console.log(todos)
})
185
Working with MySQL
To pass multiple values, just put more elements in the array you pass as the second
parameter:
const id = 223
const author = 'Flavio'
connection.query('SELECT * FROM todos WHERE id = ? AND author = ?', [id, author], (error,
todos, fields) => {
if (error) {
console.error('An error occurred while executing the query')
throw error
}
console.log(todos)
})
const todo = {
thing: 'Buy the milk'
author: 'Flavio'
}
connection.query('INSERT INTO todos SET ?', todo, (error, results, fields) => {
if (error) {
console.error('An error occurred while executing the query')
throw error
}
})
If the table has a primary key with auto_increment , the value of that will be returned in the
results.insertId value:
const todo = {
thing: 'Buy the milk'
author: 'Flavio'
}
connection.query('INSERT INTO todos SET ?', todo, (error, results, fields) => {
if (error) {
console.error('An error occurred while executing the query')
throw error
}}
const id = results.resultId
console.log(id)
)
186
Working with MySQL
When you need to terminate the connection to the database you can call the end() method:
connection.end()
This makes sure any pending query gets sent, and the connection is gracefully terminated.
187
Difference between development and production
You can have different configurations for production and development environments.
Node assumes it's always running in a development environment. You can signal Node.js that
you are running in production by setting the NODE_ENV=production environment variable.
export NODE_ENV=production
in the shell, but it's better to put it in your shell configuration file (e.g. .bash_profile with the
Bash shell) because otherwise the setting does not persist in case of a system restart.
You can also apply the environment variable by prepending it to your application initialization
command:
This environment variable is a convention that is widely used in external libraries as well.
For example Pug, the templating library used by Express, compiles in debug mode if
NODE_ENV is not set to production . Express views are compiled in every request in
development mode, while in production they are cached. There are many more examples.
Express provides configuration hooks specific to the environment, which are automatically
called based on the NODE_ENV variable value:
app.configure('development', () => {
//...
})
app.configure('production', () => {
//...
})
app.configure('production', 'staging', () => {
//...
188
Difference between development and production
})
For example you can use this to set different error handlers for different mode:
app.configure('development', () => {
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
})
app.configure('production', () => {
app.use(express.errorHandler())
})
189