# Node.js ## Introduction to Node.js ### What is Node.js ? Node.js is an open-source and cross-platform JavaScript runtime environment. It is a popular tool for almost any kind of project! Node.js runs the V8 JavaScript engine, Google Chrome's core, outside the browser. This allows Node.js to be very performant. A Node.js app runs in a single process, without creating a new thread for every request. Node.js provides a set of asynchronous I/O primitives in its standard library that prevent JavaScript code from blocking and generally, libraries in Node.js are written using non-blocking paradigms, making blocking behavior the exception rather than the norm. ### Why use Node.js ? Node.js is a cross-platform runtime, perfect for a wide range of use cases. Its huge community makes it easy to get started. It uses the V8 engine to compile JavaScript and runs at lightning-fast speeds. Node.js applications are very scalable and maintainable. Cross-platform support allows the creation of all kinds of applications - desktop apps, software as a service, and even mobile applications. Node.js is perfect for data-intensive and real-time applications since it uses an event-driven, non-blocking I/O model, making it lightweight and efficient. With such a huge community, a vast collection of Node.js packages is available to simplify and boost development. ![I/O Examples](https://i.imgur.com/OtkVxIP.png) The diagram shows that a single node process (the hexagon in the middle) can act as the broker between all of the different I/O endpoints (orange and purple represent I/O). Usually building these kinds of systems is either: difficult to code but yields super fast results (like writing your web servers from scratch in C) easy to code but not very speedy/robust (like when someone tries to upload a 5GB file and your server crashes) Node's goal is to strike a balance between these two: relatively easy to understand and use and fast enough for most use cases. Node isn't either of the following: * A web framework (like Rails or Django, though it can be used to make such things) * A programming language (it uses JavaScript but Node itself isn't a language). Instead, Node is somewhere in the middle. It is: * Designed to be simple and therefore relatively easy to understand and use * Useful for I/O-based programs that need to be fast and/or handle lots of connections At a lower level, Node can be described as a tool for writing two major types of programs: * Network programs using the protocols of the web: HTTP, TCP, UDP, DNS, and SSL * Programs that read and write data to the filesystem or local processes/memory What is an "I/O based program"? Here are some common I/O sources: * Databases (e.g. MySQL, PostgreSQL, MongoDB, Redis, CouchDB) * APIs (e.g. Twitter, Facebook, Apple Push Notifications) * HTTP/WebSocket connections (from users of a web app) * Files (image resizer, video editor, internet radio) Node does I/O in a way that is asynchronous which lets it handle lots of different things simultaneously. For example, if you go down to a fast food joint and order a cheeseburger they will immediately take your order and then make you wait around until the cheeseburger is ready. In the meantime, they can take other orders and start cooking cheeseburgers for other people. Imagine if you had to wait at the register for your cheeseburger, blocking all other people in line from ordering while they cooked your burger! This is called blocking I/O because all I/O (cooking cheeseburgers) happens one at a time. Node, on the other hand, is non-blocking, which means it can cook many cheeseburgers at once. Node handles I/O with: callbacks, events, streams, and modules. If you learn how these four things work then you will be able to go into any module in Node core and have a basic understanding of how to interface with it. You'll learn more about these topics with your readme research and as we progress. ### History of Node.js Node.js was written initially by Ryan Dahl in 2009, about thirteen years after the introduction of the first server-side JavaScript environment, Netscape's LiveWire Pro Web. The initial release supported only Linux and Mac OS X. Its development and maintenance were led by Dahl and later sponsored by Joyent. ### Node.js vs Browser Both the browser and Node.js use JavaScript as their programming language. Building apps that run in the browser is entirely different from building a Node.js application. Even though it's always JavaScript, some key differences make the experience radically different. Node doesn't have: * a DOM (hallelujah) * a window object, etc. (global variables are stored in the global object.) because it's not run in the browser and doesn't have access to browser APIs! ### Running Node.js Code If you type `node` into your command line (once you install Node.js) you will see a `>` prompt. You can now type JavaScript into your command line to experiment with JavaScript code. Press ctrl + c to exit the REPL mode. The command-line command `node path_to_program.js` also allows you to execute JavaScript programs you've written (that don't use browser features) and will print what the program returns to the console. We recommend using this to test your code to get more comfortable with your command line. ## Modules We split our code into different files to maintain, organize and reuse code whenever possible. A module system allows us to split and include code and import code written by other developers whenever required. In simple terms, a module is nothing but a JavaScript file. There are three types of modules in Node.js: 1. Core Modules: Node has a small core group of modules (commonly referred to as 'node core') that are presented as the public API that you are intended to write programs with. For working with file systems there is the `fs` module and for networks, there are modules like `net` (TCP), `http`, `dgram` (UDP). In addition to `fs` and network modules, there are many other base modules in the Node core. There is a module for asynchronously resolving DNS queries called `dns`, a module for getting OS-specific information like the tmpdir location called `os`, a module for allocating binary chunks of memory called `buffer`, some modules for parsing URLs and paths (`url`, `querystring`, `path`), etc. Most if not all of the modules in Node core are there to support Node's main use case: writing fast programs that talk to file systems or networks. We will mainly be using the following core modules this week: * fs * http * path * querystring * url The other types of modules: 2. third-party modules - there are thousands of open-source, 3rd-party Node modules created by other people. 3. your own modules! ### CommonJS Modules vs ES Modules CommonJS and ES (EcmaScript) are module systems used in Node. CommonJS is the default module system. However, a new module system was recently added to NodeJS - ES modules. CommonJS modules use the `require()` statement for module imports, and `module.exports` for module exports while it's `import` and `export` for ES modules. Visit the following resources to learn more: * [CommonJS vs ESM](https://blog.logrocket.com/commonjs-vs-es-modules-node-js/) * [Using CommonJS](https://www.javascripttutorial.net/nodejs-tutorial/nodejs-modules/) * [Using ES Modules](https://blog.logrocket.com/es-modules-in-node-today/) * [Using Modules](https://www.youtube.com/watch?v=pP4kjXykbio) * [CommonJS vs. ES Modules: Modules and Imports in NodeJS](https://reflectoring.io/nodejs-modules-imports/) ## NPM You can download useful 3rd-party modules (also known as "packages") from the Node Package Manager (npm). It's a tool, installed with Node, for managing Node's ecosystem of modules in your projects. It allows us to install tools, and packages as dependencies for our projects, and also publish our packages. Anyone can create a new Node module that adds some functionality and publish it to npm. As of the time of this writing, there are over 2 million modules on npm [resource here](http://www.modulecounts.com/). npm comes with its command-line interface you can use in your terminal while within your relevant project folder. Its main commands are: * `npm init`: Initialise a package and create a `package.json` with the definition of that package. * `npm search MODULE_NAME`: Search a module in the npm registry. * `npm install MODULE_NAME`: Install MODULE\_NAME locally. * `npm install -g MODULE_NAME`: install MODULE\_NAME globally. * `npm install MODULE_NAME`: install MODULE\_NAME locally and add it as a dependency in the package.json. * `npm install -D MODULE_NAME`: install MODULE\_NAME locally and add it as a development dependency in the package.json. i.e. a dependency that is only needed for development and not the live version of the project. ### package.json You can initialize Node within your project by calling `npm init` in the terminal. You will get a series of prompts, feel free to press enter through them or answer them. The `package.json` created by `npm init` contains meta-information about your project, including any third-party modules you install (with the use of `npm install`), and will install a Node virtual environment within your project under a folder called `Node_modules`. This is where all the Node modules are located in your project. ### Installing modules If you use `npm install MODULE_NAME` now it will install the named module into your project. You will now be able to find it in the `node_modules` folder and it will be listed under the dependencies in your `package.json`. Be sure to add `node_modules` to your list of files to ignore on .gitignore or it will be pushed up when you commit to Git Hub. When you deploy your project on a server, it will use the `package.json` to install its Node virtual environment and `node_modules` folder, so no need to worry! note: use global module installations sparingly and only for development purposes. Unless you install them locally to the project, they won't appear in your `package.json` and so won't be installed on the server when you deploy the project. ## Error Handling ## Asynchronous Programming ## Working with Files In Node.js, working with files is done using the `fs` module, which provides a set of APIs for interacting with the file system on your computer. Here are some common tasks you might need to do when working with files: 1. **Reading a file:** To read the contents of a file, you can use the `fs.readFile()` method. This method takes the path to the file as its first argument, and a callback function as its second argument. The callback function is called with two arguments: an error object (if there was an error reading the file), and the data read from the file. ```javascript const fs = require('fs'); fs.readFile('myfile.txt', 'utf8', (err, data) => { if (err) { console.error(err); return; } console.log(data); }); ``` 2. **Writing to a file:** To write data to a file, you can use the `fs.writeFile()` method. This method takes the path to the file as its first argument, the data to write as its second argument, and a callback function as its third argument. The callback function is called with an error object (if there was an error writing the file). ```javascript const fs = require('fs'); fs.writeFile('myfile.txt', 'Hello, world!', (err) => { if (err) { console.error(err); return; } console.log('File written successfully!'); }); ``` 3. **Working with directories:** To create a new directory, you can use the `fs.mkdir()` method. To remove a directory, you can use the `fs.rmdir()` method. To list the contents of a directory, you can use the `fs.readdir()` method. All of these methods take a path as their first argument and a callback function as their second argument. ```javascript const fs = require('fs'); // Create a new directory fs.mkdir('mydir', (err) => { if (err) { console.error(err); return; } console.log('Directory created successfully!'); }); // Remove a directory fs.rmdir('mydir', (err) => { if (err) { console.error(err); return; } console.log('Directory removed successfully!'); }); // List the contents of a directory fs.readdir('mydir', (err, files) => { if (err) { console.error(err); return; } console.log(files); }); ``` These are just a few examples of the tasks you can perform with the `fs` module in Node.js. There are many other methods available for working with files and directories, so be sure to check the [official Node.js documentation](https://nodejs.org/api/fs.html) for more information. ### Another example of how to use the `fs.readFile()` and `fs.writeFile()` methods: ```javascript const fs = require('fs'); // Read the contents of a file fs.readFile('example.txt', 'utf8', (err, data) => { if (err) throw err; console.log(data); }); // Write data to a file fs.writeFile('example.txt', 'Hello, world!', 'utf8', (err) => { if (err) throw err; console.log('File written successfully!'); }); ``` In this example, we first `require` the `fs` module to access its methods. Then, we use `fs.readFile()` to read the contents of a file called `example.txt`. The second argument to this method specifies the encoding for the file (in this case, `'utf8'`), and the third argument is a callback function that will be called with the file data once it has been read. If there is an error reading the file, the callback function will receive an error object. Next, we use `fs.writeFile()` to write the string `'Hello, world!'` to the same file. The second argument specifies the data to be written, and the third argument is the encoding. The fourth argument is a callback function that will be called once the file has been written. If there is an error writing the file, the callback function will receive an error object. Note that these methods are asynchronous, meaning that they don't block the execution of the rest of the code while they are running. Instead, they take a callback function that is called once the operation is complete. ~~If you need to perform multiple file operations in a specific order, you can chain them together using the callbacks or use promises or `async/await` syntax.~~ ## Command Line Apps ### Command Line Args - process.argv ## HTTP Server The Node.js `http` core module is a built-in module that provides a set of functions and classes for creating HTTP servers and clients. It allows developers to build web applications, APIs, and other networked services using Node.js. Here are some of the main features and components of the `http` module: * **HTTP Server:** The `http.createServer()` method is used to create an HTTP server. It takes a request listener function as an argument, which will be called every time a new request is received by the server. * **Request Object:** When a new request is received by the server, Node.js creates a new `http.IncomingMessage` object, which represents the incoming request. This object contains information about the request, such as the HTTP method (GET, POST, etc.), headers, and request body. * **Response Object:** The `http.ServerResponse` object is used to send a response back to the client. This object contains methods for setting headers, writing data to the response body, and ending the response. * **Routing:** The `http` module does not provide built-in routing capabilities, but it is often used in combination with other modules, such as express, to create a full-featured web framework with routing support. * **Client Requests:** The `http` module also provides methods for making HTTP requests from a client. The `http.request()` method is used to create a new client request, and the `http.get()` method is a convenience method for making a GET request. Here's a sample code to create a simple HTTP server using the http module: 1. Import the `http` module using the `require()` function: ```javascript const http = require('http'); ``` 2. Use the `http.createServer()` method to create a new HTTP server. This method takes a callback function as an argument that will be executed whenever a new request is received by the server: ```javascript const server = http.createServer((req, res) => { // request handling code goes here }); ``` 3. Inside the request handler callback function, use the `res.writeHead()` method to set the status code and headers for the response. The status code should be set to `200` to indicate a successful response, and the `Content-Type` header should be set to `text/plain` to indicate that the response body will contain plain text: ```javascript res.writeHead(200, {'Content-Type': 'text/plain'}); ``` 4. Use the `res.write()` method to write data to the response body. In this example, we'll write the string "Hello, World!" to the response body: ```javascript res.write('Hello, World!'); ``` 5. Use the `res.end()` method to end the response and send it back to the client: ```javascript res.end(); ``` 6. Finally, use the `server.listen()` method to start the server and make it listen on a specific port. In this example, we'll listen on port 3000: ```javascript server.listen(3000, () => { console.log('Server listening on port 3000'); }); ``` That's it! This simple example demonstrates how to create a basic HTTP server using the Node.js http core module. You can expand on this example by adding more routes, handling POST requests, or connecting to a database. ```javascript const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, {'Content-Type': 'text/plain'}); res.write('Hello, World!'); res.end(); }); server.listen(3000, () => { console.log('Server listening on port 3000'); }); ``` --- Here's an example of how to add routes to the previous example: ```javascript const http = require('http'); const handleRequest = (req, res) => { // set default status code and content type res.statusCode = 404; res.setHeader('Content-Type', 'text/plain'); // handle requests for the home page if (req.url === '/') { res.statusCode = 200; res.write('Hello, World!'); res.end(); } // handle requests for the about page if (req.url === '/about') { res.statusCode = 200; res.write('This is the about page'); res.end(); } // handle requests for any other page res.write('404 Not Found'); res.end(); } const server = http.createServer(handleRequest); server.listen(3000, () => { console.log('Server listening on port 3000'); }); ``` In this example, we've added some basic routing to handle requests for different pages on our website. First, we set a default status code of 404 (Not Found) and content type of plain text. This will be used for requests that don't match any of our routes. Next, we check the `req.url` property to see if the request is for the home page (`/`) or the about page (`/about`). If the request matches one of these routes, we set the status code to 200 (OK) and write a response message to the body using `res.write()`. Finally, we end the response with `res.end()`. If the request doesn't match any of our routes, we simply write a "404 Not Found" message to the response body and end the response. In this example, we've defined a separate function called `handleRequest` that contains the routing logic. We pass this function as an argument to `http.createServer()` to handle incoming requests. Note that this approach makes it easier to organize and test your code, especially if you have a large number of routes. You can also define the `handleRequest` function in a separate file and import it into your main file for better code organization. ## Testing ## Threads ## Streams ## More Debugging ### Memory Leaks #### Garbage Collection