Node.js - Express.js

From Sinfronteras
Revision as of 18:05, 21 January 2019 by Adelo Vieira (talk | contribs) (Setting up the project)
Jump to: navigation, search

Material del curso

Curso online que recomendó Remo: https://javascript30.com/

https://courses.wesbos.com/account/access/5bc5c20a93bbc3208321f21b


Guía 1 - Setting up a Node.js + TypeScript development environment: https://docs.google.com/document/d/15or9Ehxyu0AzTvYHW-LrsVxqZcLY30JjaCGhXAVmmK4/edit#

TypeScript

TypeScript is an open-source programming language developed and maintained by Microsoft. It is a strict syntactical superset of JavaScript, and adds optional static typing to the language.

TypeScript is designed for development of large applications and transcompiles to JavaScript. As TypeScript is a superset of JavaScript, existing JavaScript programs are also valid TypeScript programs. TypeScript may be used to develop JavaScript applications for both client-side and server-side.

To get started with TypeScript installation, you need to first install Node.js in your system.

Node.js

Install Node.js https://nodejs.org/en/download/ https://en.wikipedia.org/wiki/Node.js

Node.js is an open-source, cross-platform JavaScript run-time environment that executes JavaScript code outside of a browser. Historically, JavaScript was used primarily for client-side scripting, in which scripts written in JavaScript are embedded in a webpage's HTML and run client-side by a JavaScript engine in the user's web browser. Node.js lets developers use JavaScript to write Command Line tools and for server-side scripting-running scripts server-side to produce dynamic web page content before the page is sent to the user's web browser. Consequently, Node.js represents a "JavaScript everywhere" paradigm, unifying web application development around a single programming language, rather than different languages for server side and client side scripts.

Installation

https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions

curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs


Alternatively, for Node.js 10:

curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo apt-get install -y nodejs


You can check that Node.js was installed correctly by executing the following command in Bash:

node -v


Node package manager - npm

The Node.js installer will install the node command in your machine but it will also install the Node package manager (npm) command. You can also test that npm has been installed correctly by executing the following command:

npm -v

We can use the npm command to install third-party Node.js packages. The npm command allows us to install a few different kinds of dependencies:

  • Global dependencies (shared across projects)
  • Dependencies (local, not shared across projects and needed during the execution of the application, or in other words after development)
  • Development dependencies (local, not shared across the project and needed only during development)


We can install npm dependencies using the following commands:

  • Install a global dependency:
npm install -g NAME_OF_THE_DEPENDENCY
  • Install a local dependency:
npm install --save NAME_OF_THE_DEPENDENCY
  • Install a local development dependency:
npm install --save-dev NAME_OF_THE_DEPENDENCY

You can learn more about npm by reading the official documentation at https://docs.npmjs.com

Installing TypeScript and ts-node

TypeScript and ts-node can be installed using the Node Package Manager (NPM). We will install them as global dependencies:

npm install -g typescript
npm install -g ts-node

Because we have installed both dependencies as global dependencies these will be shared across all projects in our computer. This means that we only need to run these commands once.

You can learn more about npm by reading the official documentation at https://docs.npmjs.com


After installingTypeScript, the command tsc should become available:

tsc -v

Also:

ts-node -v

Git Bash in Windows

Si estamos en Windows, tenemos que instalar Git Bash in Windows; que es tan sólo un terminal Bash en Windows.

Visual Studio Code

https://code.visualstudio.com/download/

https://en.wikipedia.org/wiki/Visual_Studio_Code

Visual Studio Code (VS Code) is a source code editor developed by Microsoft for Windows, Linux and macOS. It includes support for debugging, embedded Git control, syntax highlighting, intelligent code completion, snippets, and code refactoring.

Installation

Download the <file>.deb: https://code.visualstudio.com/download Installation: https://code.visualstudio.com/docs/setup/linux

Custom icons in VS Code

There is an extension in VS Code that displays custom icons for different types of files. For example, for a JavaScript file with the “.js” extension it displays the JavaScript logo while for a TypeScript file with the “.ts” extension it displays the TypeScript logo. This can be useful because it helps your brain to visually identify each of the files in the project explorer.

You can install the extension by clicking on the Extensions icon. You can then search for vscode-icons and install it.

After installing it you will need to restart VS Code and click on File → Preferences → File Icon Theme. Then select vscode-icons.

Creating and Compiling a new TypeScript project

Creating a new TypeScript project

Each new TypeScript project requires a new empty folder. It is recommended to create a new folder under your home directory. I call this directory "CODE" and inside it, I create a new empty folder for each project.

A TypeScript project could be a Node.js project but it could also not be a Node.js project. For example, it could be an Angular or React.js project. In this section, we are going to focus on the steps required to create a TypeScript project independently of the kind of application that we will build.

All our TypeScript projects are going to need the following:

1- A new empty directory
2- A TypeScript compiler configuration file (tsconfig.json)
tsc --init
3- An npm configuration file (package.json)
npm init --y
4- At this point, we are ready to open the project using VS Code. To open a project in VS Code we need to open VS Code and select File → Open Folder...
5- After opening the folder in VS Code, we need to create a new folder. This time we will use the VS Code feature that allows us to create a new folder. I’m going to name the folder src.
6- Then I will create a new file named demo.ts using VS Code. The file will be contained inside the src directory.
7- Finally, I will add some content to the demo.ts file. In this case, we are going to add just one line of code:
console.log("Hello world!");
VisualStudio TypeScript project.png

You will need to repeat these steps everytime you create a new TypeScript project. It is very important to use a new empty folder to avoid potential configuration issues.

Compiling and running a TypeScript project

Now that we have created the basic structure of a TypeScript project we are going to compile it and execute it. To compile the TypeScript project we need to use the tsc command and the project flag to specify the TypeScript compiler configuration file:

tsc -p tsconfig.json


If everything goes well this will generate a new file named demo.js right next to the existing demo.ts file. We can then execute the JavaScript file demo.js using the node command:

node ./src/demo.js


Please note that the node command can only be used to execute JavaScript files (.js) not TypeScript files (.ts) this means that we must compile our application before we can run it. This requires two steps and it is a bit tedious. However, we can use the ts-node command to do the two steps with one single command:

ts-node ./src/demo.ts

Note that the ts-node command can be used with TypeScript files.

Creating a new TypeScript Node.js project

If we are going to work on a Node.js application we are going to need a few additional steps. First, we need to install the Node.js type definitions. We can do this using npm:

npm install --save-dev @types/node


We are also going to open the tsconfig.json and uncomment the "lib" setting and add the following value:

"lib": [ "es6" ]


At this point, we should be able to import the Node.js core modules. For example, we can import the file system module as follows:

import * as fs from "fs";

Working with dependencies

Sometimes we will need additional npm modules like for example Express. The npm modules sometimes a compatible with TypeScript automatically. Other times, we need to install additional npm module that contains the type definitions that allow us to use the module in TypeScript. In the case of Express, we need to install the express module and the @types/express module:

npm install --save express
npm install --save-dev @types/express

We can then import express as follows:

import express from "express";

This will fail if you forget to install both modules.

Running an Express application

At this point, we should be able to create a very basic Express application and execute it to see if everything has gone right so far. We are going to change the content of the demo.ts file from:

console.log("Hello world!");

To:

import express from "express";

const app = express()
const port = 3000

app.get('/', (req, res) => res.send('Hello World!'));

app.listen(
  port,
  () => console.log(`Example app listening on port ${port}!`)
);


We can then the run the Express application using:

ts-node ./src/demo.ts


If everything goes well the following should be displayed in bash:

Example app listening on port 3000!


You can then open Google Chrome and visit http://localhost:3030 and if everything went well you should see the following on the screen:

Hello World!

Once the server is running you cannot use the bash anymore unless you kill the process. You can kill the process by pressing Ctrl + c when focus on the bash.

Please note that if you change the code you will need to save the changes, kill the process using Ctrl + c and run it again with ts-node. Later in this document, we will learn a way to automatically re-load the server when the code changes but it is important to know how to do it by hand.

Automatically restart the server when the code changes

Install an npm module known as nodemon:

npm install -g nodemon

Create a nodemon.json file with the following content:

{
  "watch": ["src"],
  "ext": "ts",
  "ignore": ["src/**/*.spec.ts"],
  "exec": "ts-node ./src/demo.ts"
}


Run the application using nodemon:

nodemon

The nodemon process will use the configuration from the nodemon.json file. This configuration will watch changes in the src folder and execute the ts-node ./src/demo.ts command every time there is a change. By doing this you will not need to worry about manually killing the server and starting it again everytime you change a file.

Using the Google Chrome developer tools

We are going to use the Google Chrome developer tools to investigate the potential cause of errors and to call the server-side APIs that we will implement using Express. There are three ways to access the Chrome developer tools:

  • Clicking on the top right menu item then going to → More Tools → Developer tools.
  • Press F12
  • Press Ctrl+ Shift + I

Exploring HTTP requests

We can use the Chrome developer tools to examine all the HTTP request send from the browser. We can do this by accessing the network tab after opening the developer tools. We will then be able to see all the HTTP requests in a list:

Exploring_HTTP_requests_using_the_Google_Chrome_developer_tools

If nothing is displayed is most likely because you need to reload the page after opening the developer tools. The network tab also allows us to filter the HTTP request by the content type. For example, if we click in XHR the developer's tools will only display the HTTP requests performed as an AJAX request.

When we click on one of the HTTP requests we are able to explore the details about the requests. Including details such as the request headers, response headers, and status code.

You can learn more about the Chrome developer tools network tab at https://developers.google.com/web/tools/chrome-devtools/network-performance

Executing an HTTP call from the Chrome console

We use ts-node to run our Express applications. However, sometimes we want to call one of the REST web services that we have implemented. For example, we might want to perform the following AJAX call:

(async () => {
  const response = await fetch(
    "http://www.remojansen.com/website/js/models/awards.json"
  );
  const json = await response.json();
  console.log(json);
})();

We use the fetch function to perform an AJAX call. The best thing about fetch is that it returns a Promise, which means that we can use async/await instead of the Promise.then() method. The only problem is that our browser might not be compatible with async/await because it is one of the latest JavaScript features. So we want to compile this code into JavaScript to avoid potential compatibility issues. We can do this using the tsc command but it is a bit tedious. So instead of that, it is handy to use the TypeScript online compiler (AKA the TypeScript playground): http://www.typescriptlang.org/play/

After we write the TypeScript code we can copy the JavaScript ouput from the right-side editor. We can then open the Chrome developer tools and select the console tab. We can then paste the JavaScript code and press enter to execute it.

You can learn more about the Chrome developer tools console at https://developers.google.com/web/tools/chrome-devtools/console

TypeScript online compiler

TypeScript online compiler (AKA the TypeScript playground): http://www.typescriptlang.org/play/

Project - Implementing a REST API with TypeScript and Node.js

Please note that this CA will be continued by a second CA at the end of the second semester. The second CA will consist of a React frontend application and will use the HTTP REST API implemented as part of this CA.

Setting up the project

(5%, Marks)

In this project, we are going to implement the REST API for a for a simplified version of the website Reddit. The user will be able to create an account/ login and post links. Other users will then be able to upvote and downvote the links.

The application should be implemented using the technologies that we used in class: TypeScript, Node.js, Express.js, TypeORM, and Postgres.

To complete this practice you are going to need to install Node.js, TypeScript, and ts-node. You will also need to create a package.json file and a tsconfig.json file. You are going to need to change the default configuration in the tsconfig.json as we have been doing in class.

Please remember to refer to the guides shared in Moodle if you need additional help:

You project directory architecture should look as follows:

├───node_modules
│   └─── ...
├───package.json
├───tsconfig.json
├───app.ts
├───index.ts
├───db.ts
│   ├───backend
│   │   ├───controllers
│   │   ├───entities
│   │   ├───middleware
│   │   └───repositories
│   └───frontend
│       └─── client.ts
└───test
    └───controllers

Entities

  • Create a file named: /src/backend/entities/link.ts
This file should define the Link entity. It should contain an ID, a reference to the User an URL and a title.
  • Create a file named: /src/backend/entities/user.ts
This file should define the User entity and it must contain an ID, an email and a password.
  • Create a file named /src/backend/entities/vote.ts
This file should define the Vote entity and it should contain an ID, a reference to the user, a reference to the Link and a boolean flag that indicates if the vote is positive or negative.
  • Create a file named /src/backend/entities/comment.ts
This file should define the Comment entity and it should contain an ID, a reference to the user, a reference to the Link and a text field for the comment content.

Repositories

You must implement 3 repositories (one for each of the previously defined entities) under the /src/backend/repositories/ directory. The recommended database for this assignment is Postgres.

Controllers

(45%, 45 Marks)

We are going to need two controllers. Each endpoint has different requirements but all endpoints share the following set of common requirements:

  • In all your controllers, the inputs of each endpoint should be validated and an error HTTP 400 (Bad Request) should be returned if the inputs are invalid.
  • If an exception takes place an error HTTP 500 (Internal Server Error) should be returned.
  • When a user doesn't have permissions to perform an operation, an error HTTP 403 (Forbidden) should be returned.
  • When a request tries to access a private endpoint without a known identity, an error HTTP 401 (Unauthorized) should be returned.

Links controller

Create a new controller under the /src/backend/controllers/links_controller.ts directory. The links controller requires the following endpoints:

HTTP Method URL Description Is public
/api/v1/links GET Returns all links Yes
/api/v1/links/:id GET Returns a link and its comments Yes
/api/v1/links POST Creates a new link No
/api/v1/links/:id DELETE Deletes a link No
/api/v1/links/:id/upvote POST Upvotes link No
/api/v1/links/:id/downvote POST Downvote slink No
  • GET /api/v1/links is public and takes no arguments.
  • POST /api/v1/links requires user authentication and takes a link in the request body. It should return the new link.
  • DELETE /api/v1/links/:id requires user authentication and takes the id of a link via the request URL. A user should not be able to delete a link if he is not the owner of the link.
  • POST /api/v1/links/:id/upvote requires user authentication and takes the id of a link via the request URL. A user should not be able to vote the same link multiple times.
  • POST /api/v1/links/:id/downvote requires user authentication and takes the id of a link via the request URL. A user should not be able to vote the same link multiple times.


Auth controller

Create a new controller under the /src/backend/controllers/auth_controller.ts directory. The auth controller requires the following endpoints:

HTTP Method URL Description Is public
/api/v1/auth/login POST Returns an auth token Yes
  • POST /api/v1/auth/login is public and takes the user email and password as JSON in the request body. It returns a JWT token as a response.


User controller

Create a new controller under the /src/backend/controllers/users_controller.ts directory. The user controller requires the following endpoints:

HTTP Method URL Description Is public
/api/v1/users POST Creates a new user account Yes
/api/v1/users/:id GET Returns and user with all its activity (links and comments) Yes
  • POST /api/v1/users is public and takes the user email and password as JSON in the request body. It returns the new user as a response. An error 400 should be returned if the user email is already used by another account.
  • GET /api/v1/users/:id it is public and it takes the user ID via the URL. It should return 404 if the user is not found. If the user is found it should return not just the user but also its links and comments.


Comment controller

Create a new controller under the /src/backend/controllers/comment_controller.ts directory. The comment controller requires the following endpoints:

HTTP Method URL Description Is public
/api/v1/comments POST Creates a new comment No
/api/v1/comments/:id PATCH Updates the content of the comment No
/api/v1/comments/:id DELETE Deletes a comment No
  • POST /api/v1/comments it is private and allows us to create a new comment by sending it in the request body.
  • PATCH /api/v1/comments/:id it is private and allows us to edit an existing comment by its ID. The updated content will be sent in the request body. Users should not be able to edit comments that they don’t own. An error 400 should be thrown if the user is not the owner. An error 404 should be thrown if the comment is not found.
  • DELETE /api/v1/comments/:id it is private and allows us to delete an existing comment by its ID. Users should not be able to delete comments that they don’t own. An error 400 should be thrown if the user is not the owner. An error 404 should be thrown if the comment is not found.


Security

(20%, 20 Marks)

You must implement user authentication using JWT tokens as we have explained during the lectures of this module. The endpoint flagged as "private" must be protected by a middleware that uses JWT tokens. Please implement the JWT tokens using the "jsonwebtoken" library.

You are going to need to define a middleware named "authMiddleware" in a file named "auth_middleware.ts" under the /src/middleware directory.


Web client

(10%, 10 Marks)

You must implement a web client for one each of the endpoints previously described.


Automated test

(20%, 20 Marks)

You must implement one integration test and one unit test:

The unit test must ensure that one of the POST methods in the links controller is correct. The controller must be tested in isolation. Please implement this test using the mocha and chai libraries.

We must ensure that one of the POST methods in the links controller is correct using an integration test. Please implement this test using the "supertest" library.