© Frank Zammetti 2020
F. ZammettiModern Full-Stack Developmenthttps://doi.org/10.1007/978-1-4842-5738-8_1

1. Server-Side Action: Node and NPM

Frank Zammetti1 
(1)
Pottstown, PA, USA
 

Welcome to the book! I hope you’ve got a comfy chair under you, a tasty drink on the table next to you and perhaps a light snack (may I suggest some dark chocolate biscotti?), and your brain ready to soak up a ton of knowledge on modern web development, ‘cause that’s what the show is all about and the curtains are about to be drawn!

In this book, we’ll be building two full apps that will demonstrate all the concepts that we’ll be discussing along the way in a practical manner. Far from being just simple, contrived bits of code, these are two full apps which are functional and useful (and even fun, given that one of them is a game, which will provide you a whole new way of looking at coding). As we do so, you’ll get insight into the thinking that went into them, their design and architecture, so you get a holistic picture of what’s involved in building something like these two apps. You will even, here and there, get some notes about issues I faced and how I resolved them, things that will almost certainly help you achieve your goals as you charge onward into your own projects.

To start, we’ll look at what is most usually (though not exclusively, as you’ll learn!) the purview of the server side. Remember that we’re talking “full-stack” development here, which means you’ll be learning about coding clients as well as the server code they make use of in order to form a cohesive, whole application. In this chapter, we’ll begin by looking at two extremely popular tools for developing servers: Node.js and NPM.

Of JavaScript Runtimes and Building (Mostly) Servers

Ryan Dahl – that cat has some talent, I tell ya!

Ryan is the creator of a fantastic piece of software called Node.js (or just plain Node, as it is often written, and as I’ll write it from here on out). Ryan first presented Node at the European JSConf in 2009, and it was quickly recognized as a potential game-changer, as evidenced by the standing ovation his presentation received (I presume Ryan is an excellent presenter generally as well).

Node is a platform for running primarily, though not exclusively, server-side code that has high performance and is capable of handling large request loads with ease. It is based on the most widely used language on the planet today: JavaScript. It’s straightforward to get started with and understand, yet it puts tremendous power in the hands of developers, in large part thanks to its asynchronous and event-driven model of programming. In Node, almost everything you do is nonblocking, meaning code won’t hold up the processing of other request threads. Most types of I/O, which is where blocking comes into play most, are asynchronous in Node, whether it’s network calls or file system calls or database calls. This, plus the fact that to execute code, Node uses Google’s popular and highly tuned V8 JavaScript engine, the same engine that powers its Chrome browser, makes it very high performance and able to handle a large request load (assuming that you as the developer don’t botch things of course!).

It’s also worth noting that, as weird as it may sound, Node is single-threaded. It at first seems like this would be a performance bottleneck, but in fact, it’s a net benefit because it avoids context-switching. However, this is a little bit of a misnomer in that it’s more correct to say that Node is event-driven and single-threaded with background workers. When you fire off some type of I/O request, Node will generally spawn a new thread for that. But, while it’s doing its work, that single event-driven thread continues executing your code. All of this is managed with an event queue mechanism so that the callbacks for those I/O operations are fired, back on that single thread, when the responses come back. All of this means that there is no (or at least minimal) context-switching between threads but also that the single thread is never sitting idle (unless there is literally no work to do of course), so you wind up with that net positive benefit I mentioned.

Note

In later chapters, you’ll see that Node isn’t specific to the server side of the equation, and in fact, you don’t always build apps with Node; sometimes you use it to install and execute tools for various purposes on your own development machine. Hold on to that thought; we’ll be coming back to before long a few chapters from now.

None of these technical details are especially important to use as a Node developer, but the performance it yields is what makes it no wonder that so many significant players and sites have adopted Node to one degree or another. These aren’t minor outfits we’re talking about, we’re talking names you doubtless know, including DuckDuckGo, eBay, LinkedIn, Microsoft, Netflix, PayPal, Walmart, and Yahoo, to name just a few examples. These are large businesses that require top-tier performance, and Node can deliver on that promise (again, with the caveat that you as the developer don’t mess things up, because that’s always possible).

Node is a first-class runtime environment, meaning that you can do such things as interacting with the local file system, access relational databases, call remote systems, and much more. In the past, you’d have to use a “proper” runtime, such as Java or .Net to do all this; JavaScript wasn’t a player in that space. With Node, this is no longer true. It can compete not only on performance but also in terms of what capabilities it provides to developers. If you can think of it, chances are you can do it with Node, and that wasn’t always the case with JavaScript.

To be clear, Node isn’t in and of itself a server. You can’t just start up Node and make HTTP requests to it from a web browser. It won’t do anything in response to your requests by default. No, to use Node as a server, you must write some (straightforward and concise, as you’ll see) code that then runs on the Node “runtime.” Yes, you effectively write your own web server and app server, if you want to split hairs (or potentially FTP, Telnet, or any other type of server you might wish to). That’s a very odd thing to do as a developer – we usually apply the “don’t reinvent the wheel” mantra for stuff like that and pull one of the hundreds of existing options off the shelf. Plus, writing such servers sounds (and probably actually is) daunting to most developers, and for good reason! To be sure, it absolutely would be if you tried to write a web server from scratch in many other languages, especially if you want it to do more than just serve static content files. But not with Node!

But remember, acting as a server is just one capability that Node provides as a JavaScript runtime, and it can provide this functionality only if you, as a developer, feed it the code it needs to do so! In fact, a great many developer tools, and other types of apps, use Node as their runtime nowadays. Node really is all over the place!

Note

As you’ll see, React, Webpack, and TypeScript, three things that are primary focuses of this book (Docker being the outlier), use Node to run and/or to be installed (well, NPM is used to install them if we’re being accurate, but we’ll get to NPM in just a moment). These are tools, not servers, which is the main point: Node is useful for much more than just creating servers!

Node allows you to use the same language and knowledge on both client and server, something that was difficult to accomplish before. In fact, aside from Java and some Microsoft technologies (see project Blazor, which seeks to do the same thing with C#, if you’re curious), there never has really been an opportunity to do so until Node came along. It’s a pretty compelling opportunity.

Another critical aspect of Node is a driving design goal of the project, which is keeping its core functionality to an absolute minimum and providing extended functionality by way of APIs (in the form of JavaScript modules) that you can pick and choose from as needed. Node gets out of your way as much as possible and allows you only to introduce the complexity you really need, when you need it. Node ships with an extensive library of such modules, but each must be imported into your code, and then there are literally thousands of other modules that you can bring in as needed, some of which you’ll see as we progress throughout this book.

In addition to all of this, getting, installing, and running Node are trivial exercises, regardless of your operating system preference. There are no complicated installs with all sorts of dependencies to manage, nor is there a vast set of configuration files to mess with before you can bring up a server and handle requests. It’s a five-minute exercise, depending on the speed of your Internet connection and how fast you can type! There is also no required tooling to work with Node. In fact, a simple text editor is enough, in simplest terms (though that isn’t to say you won’t want a robust IDE with Node support later, but at least for this book I won’t be assuming anything other than Notepad or some equivalent text editor).

All of this makes working with Node so much more straightforward than many competing options while providing you with top-notch performance and load handling capabilities. Moreover, it does so with a consistent technological underpinning as that which you develop your client applications.

That’s Node in a nutshell!

Next, let’s see about getting it onto your machine so that you can start playing with some code together and we can look at Node in a little more depth.

Note

If you aren’t a JavaScript expert, don’t worry, we won’t be getting too fancy. Even when we get to the apps, I’ll consciously keep things as simple as possible. It is expected that you have some experience with JavaScript though, but you don’t need to be Brendan Eich or Doug Crockford (but if you have no experience with TypeScript, that’s fine; we’ll start from square one with it later).

First Baby Steps with Node: Installation

To get started, there’s only one address to remember:
http://nodejs.org
That’s your one-stop shop for all things Node, beginning, right from the front page, with downloading it, as you can see in Figure 1-1.
../images/491030_1_En_1_Chapter/491030_1_En_1_Fig1_HTML.jpg
Figure 1-1

Node has a simple web site, but it gets the job done!

Usually, I would tell you to install the latest version available, but in this case, it might be better to choose a long-term support (LTS) version, because they tend to be more stable. However, it shouldn’t (he said, with fingers crossed) matter which you choose, for the purposes of this book. For the record, however, I developed all the code using version 10.16.3, so if you encounter any problems, I would suggest choosing that version, which you can get from the Other Downloads link and then the Previous Releases link (you’ll be able to download any past version you like from there).

The download will install in whatever fashion is appropriate for your system, and I leave this as an exercise for the reader. For example, on Windows, Node provides a perfectly ordinary and straightforward installer that will walk you through the necessary (and extremely simple) steps. On macOS X, a typical install wizard will do the same.

Once the install completes, you will be ready to play with Node. The installer should have added the Node directory to your path. So, as a first simple test, go to a command prompt or console prompt, type node, and press Enter. You should be greeted with a > prompt. Node is now listening for your commands in interactive mode. To confirm, type the following:
console.log("Hello, you ugly bad of mostly water!");
Press Enter, and you should be greeted with something like what you see in Figure 1-2 (platform differences excepted, I’m a Windows guy myself, unashamedly, so that’s where the screenshots throughout this book will be from, perhaps with a few exceptions later).
../images/491030_1_En_1_Chapter/491030_1_En_1_Fig2_HTML.jpg
Figure 1-2

The rather uppity (though technically accurate) first greeting, proving Node is alive

If you find that this doesn’t work, please take a moment and ensure that Node is indeed in your path. It will make things a lot easier going forward.

More Useful: Executing JavaScript Source Files

Interacting with Node in CLI (Command-Line Interface) mode like this is fine and dandy, but it’s limited. What you really want to do is execute a saved JavaScript file using Node. As it happens, that’s easy to do. Create a text file named test.js (it could be anything, but that’s a pretty good choice at this point), and type the following code into it (and, of course, save it):
let a = 5;
let b = 3;
let c = a * b;
console.log(`${a} * ${b} = ${c}`);
To execute this file, assuming you are in the directory in which the file is located, you simply must type this:
node test.js
Press Enter after that, and you should be greeted with an execution, such as the one you see in Figure 1-3.
../images/491030_1_En_1_Chapter/491030_1_En_1_Fig3_HTML.jpg
Figure 1-3

It ain’t much, but it’s a real program running with Node!

Clearly, this little bit of code is unexceptional, but it does demonstrate that Node can execute plain old JavaScript just fine. It demonstrates that we’re dealing with at least the ES2015 specification as well, being more specific, thanks to the use of let and template literals (or string interpolation if you prefer). You can experiment a bit if you like, and you will see that Node should run any basic JavaScript that you care to throw at it like this.

Node’s Partner in Crime: NPM

NPM, which stands for Node Package Manager, is a companion app that installs alongside Node (though it is developed separately and can be updated on a different schedule than Node). With it, you can download packages, which are reusable JavaScript modules (and any supporting stuff they might need) from a central package registry (or a private repository if you have one). The central repository you can find at
www.npmjs.com

You can visit it through a web browser and look through all the packages available, which makes finding exactly what you need easy.

Using NPM is simple: it’s merely another command to run from a command prompt, just like Node is. For example, let’s say you create a directory named MyFirstNodeProject. In it, you execute the following:
npm install express

Here, npm is the CLI program that is NPM itself, and install is one command you can issue to it. Then, express is an argument to that command, and this is the general form that most of your interactions with NPM will take.

Note

Most NPM commands have a shorter form as well. For example, rather than type install, you can just type i, and it will do the same thing. Consult the NPM docs for these shortcuts, or be pedantic like your favorite author and always type it long-form, err, for clarity or something!

If you execute that, you’ll find that a directory called node-modules has been created, and inside it will be a lot of…well, a lot of stuff you typically don’t need to worry about too much! In short, though, it’s all the code that makes up the Express module (which doesn’t matter right now, but is a JavaScript module, or package if you prefer, which we’ll be using in the MailBag app a few chapters hence… but we’ll get to that app in due time, we’ve got a fair bit of ground to cover before then, so for now suffice it to say it’s one of the two apps we’re going to be building with the technologies discussed over the first six chapters), plus whatever modules Express itself depends on (and whatever they might depend on, and so on). NPM takes care of fetching all those dependencies for you. You’ll also notice a file named package-lock.json has been created, and for our purposes here, you don’t need to worry about that except to know not to delete it as NPM needs it to do its job.

When you use the install command like this, the modules you name are installed in the current directory, and this is referred to as the local cache, or project cache. You can also install the module into what’s called the global cache by adding an argument to the command:
npm install -g express

Now, Express will be installed in a location outside the current directory and will be shared by all Node projects (or, more precisely, it will be available to all projects, because, of course, a project won’t use a globally installed module unless you tell it to). Most usually, you will want to install dependencies in the project cache so that different projects can use different version of a given module than other projects (there is always a single version of a given module in the global cache, if any are present at all).

A Few More NPM Commands

Aside from install, there are many other NPM commands, but you’ll probably only use a subset most of the time. For example, to find out what modules are installed in your project, you can issue this command:
npm ls

Like on a *nix system, ls is short for list, and that’s what it does: lists the installed modules. What you’ll see is a textual tree that shows the modules and then the modules they depend on. In other words, more will likely be shown than just the modules you explicitly installed (some modules don’t have dependencies, but in the NPM ecosystem, those tend to be the exception rather than the rule).

Tip

One very helpful tip I can give is that if you want to see just the top-level modules, whether in the global or local cache, you can add --depth=0 to the ls command.

If you want to see what’s installed in global cache instead, you can do
npm -g ls

In fact, keep that -g option in mind because you can add that to most NPM commands to differentiate between the local and global caches.

You can also update a given module:
npm update express

Just provide the name of the module to update, and NPM will take care of it, updating to the latest available version. If you don’t provide a package name, then NPM will dutifully update all packages. And yes, you can drop a -g on it either way to target the global cache.

You can, of course, uninstall packages too:
npm uninstall express

Execute that and NPM will wipe Express from the local cache, along with its transient dependencies (so long as nothing else that remains that depends on them).

These few commands represent likely the majority of what you’ll need to interact with NPM. I refer you to the NPM docs for other commands (and note that just typing npm and hitting Enter at a command prompt will show you a list of available commands, and you can then type npm help <command> to get information about each).

Initializing a New NPM/Node Project

Now, in all of this, I did skip one step that clearly is optional but is, in fact, typical, and that’s initializing a new project. With most Node/NPM projects, you’ll also have a file named package.json in the root directory of the project. This file is the project manifest file, and it provides metadata information to NPM (and Node, at least indirectly) about your project that it needs to do certain things. It will tell NPM what modules to install if they haven’t been installed yet for one thing (which makes giving a project to another developer very easy!). It will also contain information like the name and version of the project, its main entry point, and lots of other information (most of which is optional, but we’ll look at that a bit more in the next chapter).

While you can write this file by hand or even go entirely without it, it’s a good idea to have it, and it’s a good idea to let NPM create it for you, which you can do by executing this command:
npm init
If you are following along, please make sure the directory you run this from is empty (delete node_modules and package-lock.json if present, both of which will be described later). This will trigger an interactive process that walks you through the creation of the package.json file, something like you see in Figure 1-4.
../images/491030_1_En_1_Chapter/491030_1_En_1_Fig4_HTML.jpg
Figure 1-4

Initializing a project with NPM

This will walk you through an interactive, step-by-step process wherein you can enter whichever information is relevant to your project, if any. You can just hit Enter on each option to use the default (or a blank value, whichever is applicable), or you can enter the values that are appropriate to you. For our purposes here though, you indeed can and should simply hit Enter on each prompt in the process.

Opening the generated package.json file should look something like this:
{
  "name": "temp",
  "version": "1.0.0",
  "description": "Init'ing a project",
  "main": "test.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "Frank W. Zammetti",
  "license": "ISC"
}

Adding Dependencies

Now, let’s say you want to add that Express package I mentioned to this project. There are two choices. First, you could edit package.json yourself, adding this element:
"dependencies": {
  "express": "^4.16.1"
}
However, doing just that won’t have any effect. The module isn’t installed at this point. To do that, you now must issue a command:
npm install

NPM will now (using Node as a runtime, it should be mentioned, because NPM is just a JavaScript application that runs on Node) go fetch the Express package from the central repository, will determine all the dependencies it needs, and will download and install all of them in the node_modules directory under the current directory. All these modules are now in the project cache (not global cache, it should be noted) and ready for you to use (normally, you wouldn’t use the transient dependencies of Express directly, though you certainly could, but it’s good form to declare all the modules you intend to use explicitly in package.json as dependencies).

Another alternative, and the one generally favored by developers, is not to edit the file directly and instead let NPM do it by issuing a command like this:
npm install express --save

This will cause NPM to add the dependency entry in package.json for you. This avoids the possibility of accidentally fat-fingering something and having a broken experience (or, worse, handing the project to another developer only to get the dreaded “It won’t even start up!” call).

Note

You can also replace --save with --save-dev. This results in a devDependencies entry being added to package.json. The difference is that devDependencies are modules that you only need during development, but which your project code itself doesn’t depend on. As you’ll see later, two good examples of this are TypeScript and Webpack. Also, when uninstalling dependencies, --save and --save-dev can also be used to remove the dependency from package.json.

The reason this is all important is that, now, let’s say you want to give this project to someone else. You typically do not want to provide them with all the dependencies your project requires, all the content of node_modules, if for no other reason that that directory can quickly grow to a large size. Instead, they can recreate it using the package.json file just by doing this:
npm install

That will cause NPM to read the package.json file and automatically install all the dependencies! Now, the person you’re working with has the same development environment as you as far as project dependencies go for this project without having to do any leg work themselves! Pretty sweet, right?

As you can guess, there’s quite a bit more to NPM than just what I’ve shown here, but these are the real basics.

A Quick Aside: Semantic Versioning

The dependencies section also lists the version(s) of each dependency, using a technique called semantic versioning (often called SemVer). SemVer versions are in the form major.minor.patch.

In this model, changes in the major number are meant to represent an update that contains breaking changes that would require changes to your code to remediate. Changes to the minor number are intended to constitute an update that is backward-compatible but which provides new functionality and, optionally, contains old functionality that while still functional is now deprecated and will be removed in a future release (minor number changes can also represent major internal refactoring but which produces no outward-facing changes). The patch number represents bug fix changes only.

On top of this, the tilde (~), caret (^), and asterisk (*) characters have special meaning. Tilde is used when dealing with patch versions, while caret is used when dealing with minor versions, and asterisk has the typical “wildcard” meaning you’re probably familiar with in other contexts.

To give you a very brief overview, here are some of the most common dependency versions you might see in package.json, using Express as an example:
  • “express” : “1.2.3” – NPM will grab this specific version only.

  • “express”: “~1.2.3” – NPM will grab the most recent patch version. (So, ~1.2.3 will find the latest 1.2.x version but not 1.3.x or anything below 1.2.x.)

  • “express”: “^1.2.3” – NPM will grab the most recent minor version. (So, ^1.2.3 will find the latest 1.x.x version but not 1.3.x or anything below 1.x.x.)

  • “express”: “*” – NPM will grab the newest version available. (There is also an explicit latest value that does the same thing.)

There’s quite a lot more to SemVer than this (and there’s also no shortage of criticism and folks who aren’t exactly fans of it), but this should cover the most common features you’re likely to encounter. Indeed, this should be all you will need for this book.

Note

When using the npm install <package> command, you can add @major.minor.patch after the package name to specify the version to install using all the SemVer rules described in the preceding text.

Fisher Price’s “My First Node Web Server”

Now that you know a bit about Node and NPM, the very basics at least, let’s write some actual code, beyond the simple example shown earlier, that is, and run it with Node.

When I say that Node makes writing server software trivial, that may well be the understatement of the year! Perhaps the simplest example (that does something “real,” at least) is this:
require("http").createServer((inRequest, inResponse) => {
  inResponse.end("Hello from my first Node Web server");
}).listen(80);
That remarkedly small bit of code is all it takes in Node to write a web server. Even though it’s not necessary, just for practice, go ahead and create a directory and use NPM to init it as a project (and this time, add a -y to the init command, which will use the defaults for all the prompts rather than making it interactive). Then, type that code into a file, save it as server.js. Now, at this point, you could start it up like so:
node server.js
But let’s do one more thing first. Open the generated package.json file, and in the scripts section, add a new attribute to the object:
"start": "node server.js"

What this does is it effectively defines a custom command for NPM. The start command is one that already exists, but it’s one that does nothing until you add this entry in package.json, so it may as well not exist! Once you add that entry though, NPM will look for that start key, take its value, and execute whatever the command is that you provide in it. The benefit of doing this is that every project you create with Node and NPM will be startable the same way. Without this, a developer would need to figure out what file is the main entry point to launch it with Node (and note that the main key in package.json may not be enough to tell someone this, as is the case here, since the default value of index.js would be wrong for this project).

Once you add that, go ahead and start the app:
npm start

Yep, that’s it! NPM knows what to do now and will launch Node and tell it to execute server.js.

Now fire up your favorite web browser and visit http://127.0.01. You’ll be greeted with the text “Hello from my first Node Web server”. Note, however, that if anything else on your system is already listening on port 80 then the app won’t actually start, you’ll get an error instead. In that case, simply change the listen(80) call to a free port and you’ll be good to go (and, naturally, add the port to the end of the URL in that case too).

If that isn’t a little bit amazing to you, then you’ve probably seen the Flying Spaghetti Monster (FSM) travel one too many times around your neighborhood and have been totally desensitized to the amazing! (FSM – yeah, uhh, I’m not gonna even try and explain what the FSM is; here’s a link: www.venganza.org).

Obviously, this is a simplistic example, but it should get the basic idea across well enough. But what exactly is going on in that simple example at a code level? As it happens, quite a bit, and most of it is key to how Node works.

The first concept is the idea of importing modules. In the example code, http is a module. This is one of the core Node modules that Node comes with out of the box, and, as such, it is compiled directly into the Node binary. Therefore, you won’t find a separate JavaScript file for it in the Node installation directory for it. This is true of all the Node core modules, all of which you can find in the Node documentation on the Node site. To import any of them, you just require() them by name.

Note

We’ll look at some of the more commonly used modules in the next chapter.

You can create your own modules too just by adding other .js files to your project and require()-ing them. This gets a little more involved, with discussions of things like scope and exports, and we’ll get to all of that in time. But for now, I wanted to mention it at least in case you really are entirely new to Node so that you can find the appropriate section in the Node docs to describe this if you want to jump ahead.

The require() function returns an object that is essentially the API provided by the module. This object can include methods, attributes, or whatever you want. In fact, it could conceivably be just a variable with some data in an array. More times than not, though, it will be an object with some methods and attributes. In the case of http in this example, one of the methods the object returned is createServer(). This method creates a web server instance and returns a reference to it.

The argument you pass to this method is a function that serves as a request listener; that is, the function executed any time a request is made to the server.

This function handles all incoming HTTP request. You can do anything you need to there, including such things as the following:
  • Interrogate the incoming request to determine the HTTP method.

  • Parse the request path.

  • Examine header values.

You can then perform some branching logic on any or all of these, perhaps access a database or other durable storage mechanism, and return an appropriate and fully dynamic response for the specific request.

Creating a web server alone won’t actually do anything. It won’t respond to requests until you do a little more work. As mentioned, the createServer() method returns a reference to the web server instance, which itself contains the method listen(). That method accepts a port number on which the server should listen and, optionally, the hostname/IP address on which to listen. In the example, the standard HTTP port 80 is specified, and by default, the local machine loopback address 127.0.0.1 is used if no IP address is specified, as is the case here. Once you call this method, the server will begin listening for requests (i.e., assuming nothing else is already using that port on your system!), and for each request that comes in, it will call the anonymous function passed to createServer().

This callback function (callback functions being one mechanism by which Node provides nonblocking functionality, the others being Promises and async/await) receives two arguments, inRequest and inResponse, which are objects representing the HTTP request and response, respectively. In this simple example, all this callback function does is call the end() method on the response object, passing the response you want to send back. By default, an HTTP 200 response code header will be added automatically, so this completes the handling of a given request.

With just this little bit of code, you, in fact, know the basics of what you would require for writing a server for the MailBag app later! But, when we get to that app, we’ll use something a little more robust (Express, as mentioned earlier), but this gives you a fundamental idea of what it takes.

Bonus Example

Let’s take this web server code just a little further to roll in some more NPM goodness, just to use almost everything discussed so far.

To begin, let’s add a dependency to our project. We’re going to use the not-very-creatively-but-very-accurately-named request module, which will provide to our server an elementary HTTP client for it to use to make remote calls:
npm install request --save
With that done, copy that server.js file and name it server_time.js, then replace its contents with the following code:
require("http").createServer((inRequest, inResponse) => {
  const requestModule = require("request");
  requestModule(
    "http://worldtimeapi.org/api/timezone/America/New_York",
    function (inErr, inResp, inBody) {
      inResponse.end(
        `Hello from my first Node Web server: ${inBody}`
      );
    }
  );
}).listen(80);

As you probably recognize, it’s the same code as before, but now with a bit more inside the callback function provided to createServer(). What we’re doing now is firstly to import the request module and to give it the name requestModule (just to help disambiguate it from the inRequest object passed into the callback function). The API for this module is straightforward: pass a URL to the constructor, plus a callback, and a call will be made to the URL, and the provided callback will be executed when the response to that call comes back. The URL here is to the World Time API, which you can read about here: http://worldtimeapi.org. This particular form of the URL (the API provides a few) takes in a time zone, America/New_York here (though you should certainly feel free to replace that with a time zone you prefer – you can access http://worldtimeapi.org/api/timezone in a browser to see a list of available time zones). What we get back is a chunk of JSON, which is then written to the response that is returned to the browser.

The final step is you’ll need to edit package.json to change that start value to indicate the new JavaScript file, after which you can launch the app and try it. The response you’ll see now isn’t necessarily pretty:
Hello from my first Node Web server: {"week_number":36,"utc_offset":"-04:00","utc_datetime":"2019-09-06T17:22:45.406437+00:00","unixtime":1567790565,"timezone":"America/New_York","raw_offset":-18000,"dst_until":"2019-11-03T06:00:00+00:00","dst_offset":3600,"dst_from":"2019-03-10T07:00:00+00:00","dst":true,"day_of_year":249,"day_of_week":5,"datetime":"2019-09-06T13:22:45.406437-04:00","client_ip":"12.198.42.69","abbreviation":"EDT"}

…but it gets the job done (and, if like me, you have a browser extension that automatically “prettifies” JSON, then what you’ll see will be, well, prettier!).

This is a good example not only of adding dependencies to a project and using them but also of the asynchronous nature of Node I talked about earlier. Here, the call to the World Time API takes some time, and the response to the request coming from the browser is queued up and awaits that response before executing the callback passed to the requestModule() constructor, which then produces the final response to the browser. But, all that time, Node was free to handle other incoming requests; work wasn’t held up awaiting the remote request, that’s the key thing.

Summary

In this chapter, we looked at Node and NPM and discussed the very basics of their usage. But, because Node and NPM are, conceptually, pretty simple things, these basics are, by and large, all you need to write real applications. You now know how to execute JavaScript code, how to create an NPM project, and how to add dependencies. You understand the difference between the global cache and the local (project) cache, and you even know how to write a basic web server!

In the next chapter, we’ll continue looking at these two tools in a bit more detail, getting to some slightly more advanced stuff with them, to expand the foundation from which we’ll build our two apps later in the book.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.26.217