# WEEK 1 - NODEJS, Rest API and ExpressJS ###### tags: `WEB` `JS` `DIGITALIZE` `BACKEND` ## INTRODUCTION Node.js is an open-source and cross-platform JavaScript runtime environment. Node.js runs the V8 JavaScript engine, the core of Google Chrome, outside of the browser. This allows Node.js to be very performant. - A Node.js app runs in a single process with single thread. - Node.js runs non-blocking, asynchronous programming, which is very memory efficient. - Node.js provides a set of asynchronous I/O primitives in its standard library that prevent JavaScript code from blocking **How Node.js handles a file request** * Sends the task to the computer's file system. * Ready to handle the next request. * When the file system has opened and read the file, the server returns the content to the client. ### What Can Node.js Do? * Node.js can generate dynamic page content * Node.js can create, open, read, write, delete, and close files on the server * Node.js can collect form data * Node.js can add, delete, modify data in your database ## Get Started ### Nodejs You have to install Nodejs on your machine from the this official website https://nodejs.org/ Once you have downloaded and installed Node.js on your computer, let's try to display "Hello World" in a web browser. Create a Node.js file named "app.js", and add the following code: ``` var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/html'}); res.end('Hello World!'); }).listen(8080); ``` ### NPM npm stands for node package manager. It allows for seamless node.js package management. You can manage packages of your projects like install, update and delete node.js packages. **Installing npm** It comes with Nodejs. To check npm installed in your machine, open a CMD and run the following command: `npm -v` **package.json** The package.json is the project manifest file. Using package.json you can manage dependencies and write scripts. It has all the meta data about the project. creating `package.json`: `npm init` It will ask you to provide some information about the project ### NPM Usage Installing packages - Locally: A locally installed package can be accessed only on the same project. `npm i <package_name>` Example: `npm i express` - Globally: globally installed packages works anywhere on the machine. `npm i -g <package_name>` To update a package: `npm update <package_name> ` For globall package: `npm update <package_name> -g` To uninstall the package: `npm uninstall <package_name>` For globally: `npm uninstall <package_name> -g` To install all the packages required for the project and listed in package.json, use the following command: `npm i` ## Modules ECMAScript modules are the official standard format to package JavaScript code for reuse. Modules are defined using a variety of import and export statements. **Write your own simple module** ``` // sum.js function sum(x,y) { return x + y; } export sum;// export {sum}; ``` imports the function from `sum.js`: ``` // app.JS import { sum } from './sum.js'; console.log(sum(4,2)); ``` **Note:** Node.js has two module systems: `CommonJS` `modules` and ECMAScript modules. **Enabling modules/require** - if you don't have `package.json`, create new on at root directory of the project. - add new property/value at the top of the file: ``` { "type":"module", "dependencies": { "nodemon": "^2.0.20" }, "scripts": { "server": "nodemon app.js" } } ``` Now you can use import/export in your project. - If you need to use two system modules in the same file, then you can do the following: ``` import { createRequire } from "module"; const require = createRequire(import.meta.url); //..... import {sum} from './helper.js' // now use require as you want let http = require('http') ``` ### HTTP built-in module Node.js has a built-in module called HTTP, which allows Node.js to transfer data over the Hyper Text Transfer Protocol (HTTP). To include the HTTP module, use the require() method: ``` var http = require('http'); ``` Node.js as a Web Server The HTTP module can create an HTTP server that listens to server ports and gives a response back to the client. Use the createServer() method to create an HTTP server: ``` // app.js var http = require('http'); //create a server object: http.createServer(function (req, res) { res.write('Hello World!'); //write a response to the client res.end(); //end the response }).listen(8080); //the server object listens on port 8080 ``` Run the server: `node app.js` ### HTTP Header **Content Type Response** If you need to specify the type of returned content, you have to include 'Content-Type' Http header before respond to the request: ``` var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/html'}); res.write('Hello World!'); res.end(); }).listen(8080); ``` ### Query String The function passed into the http.createServer() has a req argument that represents the request from the client, as an object (http.IncomingMessage object). This object has a property called "url" which holds the part of the url that comes after the domain name: ``` var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/html'}); res.write(req.url); res.end(); }).listen(8080); ``` Visit the server via browser http://127.0.0.1?key=value you will get response returned with `?key=value`. ### Query String Splitting There are built-in modules to easily split the query string into readable parts, such as the URL module, which is `var url = require('url');` ``` http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/html'}); var q = url.parse(req.url, true).query; var txt = q.year + " " + q.month; res.end(txt); }).listen(8080); ``` Output: `` ## Hot Reload We may need automatically restarting the node application when file changes in the directory are detected rather than stop the server and start it again. **Using Nodemon Tool** Steps: - Install the tool locally `npm i nodemon` - Config run script for nodemon, go to `package.json` and add the following line ``` "scripts": { "dev": "nodemon app.js" } ``` - Now run the app with `npm run dev` ## File System Node.js as a File Server Common use for the File System module: Read files Create files Update files Delete files Rename files The Node File System (fs) module can be imported using the following syntax: ``` var fs = require("fs") ``` ### Reading File **Example** Create a text file named `data/input.txt` with the following content ``` This is line 1 This is line 2 ``` Let us create a js file named app.js with the following code: ``` var fs = require("fs"); // Asynchronous read fs.readFile('data/input.txt', function (err, data) { if (err) { return console.error(err); } console.log("Asynchronous read: " + data.toString()); }); // Synchronous read var data = fs.readFileSync('data/input.txt'); console.log("Synchronous read: " + data.toString()); ``` ### Open a File Following is the syntax of the method to open a file in asynchronous mode: ``` fs.open(path, flags[, mode], callback) ``` **Parameters** Here is the description of the parameters used − * path − This is the string having file name including path. * flags − Flags indicate the behavior of the file to be opened. * mode − It sets the file mode (permission and sticky bits), but only if the file was created. It defaults to 0666, readable and writeable. * callback − This is the callback function which gets two arguments (err, fd). **Note:** See more about flags for opening the files. Example: ``` var fs = require("fs"); // Asynchronous - Opening File console.log("Going to open file!"); fs.open('input.txt', 'r+', function(err, fd) { if (err) { return console.error(err); } console.log("File opened successfully!"); }); ``` ### Writing a File Syntax: ``` fs.writeFile(filename, data[, options], callback) ``` This method will over-write the file if the file already exists. Parameters * Here is the description of the parameters used − * path − This is the string having the file name including path. * data − This is the String or Buffer to be written into the file. * options − The third parameter is an object which will hold {encoding, mode, flag}. By default. encoding is utf8, mode is octal value 0666. and flag is 'w' * callback − This is the callback function which gets a single parameter err that returns an error in case of any writing error. ``` var fs = require("fs"); console.log("Going to write into existing file"); fs.writeFile('input.txt', 'Simply Easy Learning!', function(err) { if (err) { return console.error(err); } console.log("Data written successfully!"); console.log("Let's read newly written data"); fs.readFile('input.txt', function (err, data) { if (err) { return console.error(err); } console.log("Asynchronous read: " + data.toString()); }); }); ``` ### Closing a File Syntax ``` fs.close(fd, callback) ``` **Parameters** Here is the description of the parameters used − * fd − This is the file descriptor returned by file fs.open() method. * callback − This is the callback function No arguments other than a possible exception are given to the completion callback. Example: ``` var fs = require("fs"); var buf = new Buffer(1024); console.log("Going to open an existing file"); fs.open('input.txt', 'r+', function(err, fd) { if (err) { return console.error(err); } console.log("File opened successfully!"); console.log("Going to read the file"); fs.read(fd, buf, 0, buf.length, 0, function(err, bytes) { if (err) { console.log(err); } // Print only read bytes to avoid junk. if(bytes > 0) { console.log(buf.slice(0, bytes).toString()); } // Close the opened file. fs.close(fd, function(err) { if (err) { console.log(err); } console.log("File closed successfully."); }); }); }); ``` ### Delete a File Syntax ``` fs.unlink(path, callback) ``` **Parameters** Here is the description of the parameters used − * path − This is the file name including path. * callback − This is the callback function No arguments other than a possible exception are given to the completion callback. Example: ``` var fs = require("fs"); console.log("Going to delete an existing file"); fs.unlink('input.txt', function(err) { if (err) { return console.error(err); } console.log("File deleted successfully!"); }); ``` ## Project Structure Now let's review simple project structure for any nodejs application - app_root_directory - node_modules - src - index.js - assets - data - inputs.txt - package.json - package-lock.json ## Rest API Design **REST** stands for Representational State Transfer. It is architectural style. **API (Application Programming Interface):** it is interface acts as an intermediary between the client and the server. **So Restful:** Any API (Application Programming Interface) that follows the REST design principle * Client-Server: helps the client and the server components evolve independently. * user interface across multiple platforms * improve scalability * Stateless RESTful Web Service should not keep a client state on the server. This restriction is called Statelessness. The server cannot take advantage of any previously stored context information on the server. For this reason, the client application must entirely keep the session state. * Cacheable requires that a response should implicitly or explicitly label itself as cacheable or non-cacheable. If the response is cacheable, the client application gets the right to reuse the response data later for equivalent requests and a specified period. * Layered System The layered system style allows an architecture to be composed of hierarchical layers by constraining component behavior. For example, in a layered system, each component cannot see beyond the immediate layer they are interacting with. ### What is the resource in Restful? Any information that we can name can be a resource. For example, a REST resource can be a document or image, a temporal service, a collection of other resources. ### HTTP verbs method - POST: for create resource, Ex: https://rest-api-server.com/posts - GET: for retrieving resource Ex: https://rest-api-server.com/posts/{id} // Get specific resource Ex: https://rest-api-server.com/posts // Get all resources - PUT: for updating and replacing resource, Ex: https://rest-api-server.com/posts/{id} - DELETE: for deleting resource, Ex: https://rest-api-server.com/posts/{id} ### REST API Design Best Practices - **Use JSON as the Format for Sending and Receiving Data** To ensure the client interprets JSON data correctly, you should set the Content-Type type in the response header to application/json while making the request. - **Use Nouns Instead of Verbs in Endpoints** When you're designing a REST API, you should not use verbs in the endpoint paths. The endpoints should use nouns, signifying what each of them does. This is because HTTP methods such as GET, POST, PUT, PATCH, and DELETE are already in verb form for performing basic CRUD (Create, Read, Update, Delete) operations. So, for example, an endpoint should not look like this: https://rest-api-server.com/getPosts or https://rest-api-server.com/createPost Instead, it should be something like this: https://rest-api-server.com/posts - **Use Status Codes in Error Handling** You should always use regular HTTP status codes in responses to requests made to your API. This will help your users to know what is going on – whether the request is successful, or if it fails, or something else. Below is a table showing different HTTP Status Code ranges and their meanings: <table> <thead> <tr> <th>Status Code range</th> <th>Meaning</th> </tr> </thead> <tbody> <tr> <td>100 – 199</td> <td>Informational Responses. <br> For example, 102 indicates the resource is being processed</td> </tr> <tr> <td>300 – 399</td> <td>Redirects <br> For example, 301 means Moved permanently</td> </tr> <tr> <td>400 – 499</td> <td>Client-side errors <br> 400 means bad request and 404 means resource not found</td> </tr> <tr> <td>500 – 599</td> <td>Server-side errors <br> For example, 500 means an internal server error</td> </tr> </tbody> </table> - **Use Nesting on Endpoints to Show Relationships** Ex: https://rest-api-server.com/posts/{id}/comments You should avoid nesting that is more than 3 levels deep as this can make the API less elegant and readable. - **Use Filtering, Sorting, and Pagination** Sometimes, an API's database can get incredibly large. If this happens, retrieving data from such a database could be very slow. Filtering, sorting, and pagination are all actions that can be performed on the collection of a REST API. This lets it only retrieve, sort, and arrange the necessary data into pages so the server doesn’t get too occupied with requests. Ex: https://rest-api-server.com/posts?tags=javascript This endpoint will fetch any post that has a tag of JavaScript. - **Use SSL for Security** SSL stands for secure socket layer. It is crucial for security in REST API design. This will secure your API and make it less vulnerable to malicious attacks. Examples: https://rest-api-server.com/posts runs on SSL. http://rest-api-server.com/posts does not run on SSL. - **Versioning** REST APIs should have different versions, so you don’t force clients (users) to migrate to new versions. This might even break the application if you're not careful. One of the commonest versioning systems in web development is semantic versioning. Ex: https://rest-api-server.com/v1/ for version 1 https://rest-api-server.com/v2/ for version 2 - **API Documentation** When you make a REST API, you need to help clients (consumers) learn and figure out how to use it correctly. The best way to do this is by providing good documentation for the API. The documentation should contain: * Relevant endpoints of the API * Example requests of the endpoints * Implementation in several programming languages * Messages listed for different errors with their status codes One of the most common tools you can use for API documentation is Swagger. And you can also use Postman, one of the most common API testing tools in software development, to document your APIs. ### What is a CRUD? CREATE, READ, UPDATE, and DELETE are the acronyms for CRUD. In a REST environment, here's how to use CRUD: * Create Use the HTTP POST method to build a resource in a REST environment. * Read Use the GET method to read a resource. Reading data from a resource retrieves it without changing anything. * Update To make changes to a resource, use the PUT method. * Delete Use the DELETE method to remove a resource from the system. ## Express JS The most popular Node.js web framework is Express.js. Building a Node.js backend from the ground up for an application can be complex and time-consuming. The boilerplate code, which includes everything from port configuration to route handlers, takes time away from what matters most: creating the application's business logic. Developers can save time and focus on other vital activities by adopting web frameworks like Express.js. ### Prerequisites - Basics of JS. - Basics of Nodejs. - Installed Nodejs and NPM. ### Setting Up an Express.js Server - Create a directory for the project to live in to set up a Node.js application and an Express.js server. `npm init -y`: this command used for creating a ready `package.json` file. - Install `express.js`: `npm i express` Our dependencies have now been loaded into node_modules and package.json. - Create an `src/index.js` file that will serve as the server's entry point. ``` const express = require('express') const bodyParser = require('body-parser') const app = express() const port = 3000 app.use(bodyParser.json()) app.use( bodyParser.urlencoded({ extended: true, }) ) ``` - Now create a route to look for a GET request on the root (/) URL and return some JSON ``` app.get('/', (request, response) => { response.json({ success: true, message: 'Hello' }) }) ``` - Makes the server listen to a port 8090: ``` const PORT = 8090; app.listen(PORT, () => { console.log(`Server running on port ${PORT}.`) }) ``` - Config `package.json` to run app from `src/index.js`: ``` "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "node src/index.js" }, ``` In your browser's URL bar, type http://localhost:8090 to see the JSON we created previously. ``` { "success": true, "message": "This is Nodejs and express App" } ``` **Source Code Notes**: - Express `body-parser` is an npm library used to process data sent through an HTTP request body. It exposes four express middlewares for parsing text, JSON, url-encoded and raw data set through an HTTP request body. These middlewares are functions that process incoming requests before they reach the target controller. - `app.use()` acts as a middleware in express apps. - `bodyParser.urlencoded({ extended: true, })` supports parsing of `application/x-www-form-urlencoded` post data - `app.get(routePath, callback)` used to make the server listen on any request income and match routePathm in the above example any request from a client income and match http://localhost:8090/ will routed to `app.get` and responds with defined JSON. ### CRUD Operations Create all of the functions for each route first. Then, to make the functions accessible, export them as follows: * GET / - home() * GET /posts - getPosts() * GET /posts/:id - GetPostById() * POST /posts - createPost() * PUT /posts/:id - UpdatePost() * DELETE /posts/:id - deletePost() - Create `PostController.js` at `src/` directory. - add static list of data ``` let posts = [ { id:1, title:'Post Title', content:'content of the post' }, { id:2, title:'Title 2', content:'content of the post' }, { id:3, title:'Title 3', content:'content of the post' }, { id:4, title:'Title 4', content:'content of the post' }, { id:5, title:'Title 5', content:'content of the post' } ]; ``` - Prepare Responser JS file to make our API nicely, create `responserApi.js` in `src` directory with following code: ``` const success = (message, results, statusCode) => { return { message, success: true, code: statusCode, results }; }; const error = (message, statusCode) => { return { message, code: statusCode, success: false }; }; module.exports = { success, error } ``` - Now let's include responserApi.js functions into controller js file and start write functions: - Require `responseApi.js` - Fetch all posts ``` const getPosts = (request, response) => { response.status(200).json(success(posts)) } ``` - To get specific post by ID: ``` const getPostById = (request, response) => { const id = parseInt(request.params.id) let post = posts.find((item)=>item.id == id) if(!post) response.status(404).json(error('Post Not Found',404)) else{ response.status(200).json(success(post)) } } ``` Complete project on this https://github.com/computiq-training/dig-w1-express.git repo. ## Bonus ### How to add .gitignore for nodejs project? - Go to https://www.toptal.com/developers/gitignore - Search for "Node". - Copy the content of the file. - Go to your project, create a file `.gitignore` at root directory and paste the content that you copied in previous step. - Save Now `node_modules` is one of the directories that will not included while pushing the changes to the github server. **Note:** It is peter to do this step at init git for the project and before doing any adding files or staging. ## References https://expressjs.com/ https://restfulapi.net/