---
marp: true
paginate: true
math: mathjax
theme: buutti
title: Express Basics
---

# Express Basics

<!-- headingDivider: 5 -->
<!-- class: invert -->

# JS HTTP server

# A simple HTTP server in Node

A simple server in three steps:
- include "http" library
- create server responses
- define the port that you want to listen to.

Note: The `_` before a variable name is used to indicate that the variable isn't used. This is to avoid the VSCode warning about unused variable.

---

```ts
import http from 'http'

const server = http.createServer((\_req, res) => {
  res.write('Some cool response!')
  res.end()
})

const port = 3000
server.listen(port)

console.log('HTTP Server listening port', port)
```

For this code to work, remember "type": "module" in your package.json. Also, you may need to change your port number to something other than 3000.

# PORT numbers

The port number (16 bit unsigned integer) defines a port through which the communication to your application happens. Ports allow multiple applications to be located on the same host (IP).

The client connects to this port in some server (defined by an IP address), so the client and the server are able to communicate.

Computers have several different ports at their disposal but some of them are being used and your application might not be able to assign those ports to itself.

---

<div class='columns' markdown='1'>
<div markdown='1'>

Ports from 0 to 1023 are the system ports. There are restrictions in manually assigning applications to use them.

Generally speaking, ports from 1024 to 65535 are available for user to assign applications to use them.

</div>
<div markdown='1'>

![](imgs/1-express-basics_0.png)

</div>
</div>

# Request and response

The request object is an object that is sent ***from the client to the server.***

The response object is an object that is sent ***from the server to the client***. When you modify the response object, you're essentially modifying how the server responds to the client.

```ts

const server = http.createServer((req, res) => {
  res.write("Some cool response!");
  res.end();
});
```

# Exercise 1: Simple Request & Response
<!--_class: "exercise invert" -->

Create a basic HTTP-server application. The application should respond to GET requests by returning a response with the following content: "Hello world!".

Test your application either with client such as Insomnia or Postman, or by navigating your browser to `http://localhost:<your_portnumber>`



# Express Server

# Express

Express is the most popular server-library for Node

Setting up a server with node-http is already fairly simple, but Express tries to make this even simpler

In some cases you can manage without Express, but for larger applications it's recommended

Install Express by using

```
npm install express
```

# A side note about dependencies

* By using `npm install <package>`, the package will become dependency for the application.
* By using `npm install --save-dev <package>`, the package will become *a development dependency* for the application.
  * This means the package won't be included in the built application.
* A shortcut for `---save-dev` is `-D`
* Dependencies are needed to run the application, development dependencies are needed for development.
* Additional info:   [https://www.geeksforgeeks.org/difference-between-dependencies-devdependencies-and-peerdependencies/](https://www.geeksforgeeks.org/difference-between-dependencies-devdependencies-and-peerdependencies/)

# Getting started with Express

Initialize a new TypeScript project with appropriate `tsconfig.json` file and scripts for building and/or running the project in development mode using `ts-node`.

Create a very simple express project in `index.ts`

```ts
import express from 'express'

const server = express()

server.listen(3000, () => {
  console.log('Listening to port 3000')
})
```

Then start the server as you would any other TS project and connect to http://localhost:3000 with your browser or using an API tool such as Insomnia or Postman.


# Express - Requests

The server does not yet *do* anything except listens. That is because we have not defined how it should deal with incoming **requests**.

With Express you can easily process different kind of requests, e.g. GET, POST, DELETE etc.

An example of how to handle a GET request to the root of the project (`/`):

```ts
import { Request, Response } from "express";

server.get("/", (req: Request, res: Response) => {
  res.send("Just saying hello!")
})
```

## TypeScript Request and Response

Notice how we import `Request` and `Response` from `express`. These types are defined in express, but identically named types are also available from JavaScript basic library.

If you use the Request and Response types without importing them, JS mistakenly thinks they are different objects, and complains about incompatibility.



# Express - Endpoints

To create a new **endpoint** we define what *kind*  of request we're handling, then what the *route* to the endpoint is and finally what the endpoint *does*.

In the below example we are creating an endpoint that handles a GET request to route `/foobar`, that sends a response containing the string 'OK'.
```ts
server.get('/foobar', (req, res) => {
  res.send('OK')
})
```

# Nodemon

Reminder  : If you are not using Nodemon in development, you are making your life unnecessarily difficult. Use Nodemon. 

Instructions are   below as a refresher.

# Nodemon with JavaScript

One of the handiest packages out there is nodemon, which monitors your node program. Whenever you save your files, it reloads the program, showing you immediately the results of your changes.

Nodemon is installed as a dev dependency	 npm install --save-dev nodemon

To use nodemon, you should have a development script	"dev": "nodemon ./index.js"

# Nodemon with TypeScript

To get Nodemon working with TypeScript we need additional package to run our TS code without waiting for it to compile.	 npm install --save-dev nodemon ts-node

After installing, nodemon works directly with .ts files.	"dev": "nodemon ./index.ts"

# Exercise 2: Simple Express Server
<!--_class: "exercise invert" -->

Create a basic Express application. The application should respond to GET requests by returns a response with the following content: "Hello world!".

**Extra**: Add a new endpoint to your application. So, in addition to accessing http://localhost:3000, try to send something back from http://localhost:3000/endpoint2.

<!-- ^ Tuon extran ei pitäisi olla extra, vaan perustehtävä. Extraksi ehkä toisen serverin käynnistäminen eri porttiin samanaikaisesti -->

# Request params & query

If request params are defined, they're something user **must** send to the server. You can think parameters as the "path" of the endpoint.

https://localhost:5000/ThisIsParam

Request query is **optional** information that the user can send to the server. A question mark (`?`) is used to separate the request query.

https://localhost:5000?ThisIsQuery=123

Chaining query parameters is done with et symbol (`&`)

https://localhost:5000?first=123&second=456

# Request params & request query

```ts
app.get("/:name/:surname", (req: Request, res: Response) => {
  console.log("Params:");
  console.log(request.params);
  console.log("Query:");
  console.log(request.query);
  response.send("Hello " + request.params.name);
});

app.listen(5000);
```

Try connecting via browser!

http://localhost:5000/John/Doe
http://localhost:5000/John/Doe?query1=123


# Request query & param types

Request parameters and query parameters are always of type **string**. If a number is passed, it will be a string that contains a number. 

The example below will always return 404, since the strict equality `item.id === id` will never be true. This is because the `item.id` is of type **number**  and `id` being of type **string**.

---

```ts
import express, { Request, Response } from 'express'

const server = express()

const data = [
  { id: 1, name: 'John' },
  { id: 2, name: 'Jane' }
]

server.get('/:id', (req: Request, res: Response) => {
  const id = req.params.id
  const info = data.find(item => item.id === id)
  if (info === undefined) {
    return res.status(404).send()
  }

  res.send(info)
})

server.listen(3000)
```


# Exercise 3: Counter Server
<!--_class: "exercise invert" -->

Create an API that consists of only one endpoint: `/counter`
Whenever you enter this endpoint from the browser, it should respond with a JSON object with information on how many times the endpoint has been accessed.

**Extra**: Make it possible to set the counter to whichever integer with a query parameter /counter?number=5



# Exercise 4: Advanced Counter Server

Expand the API from the previous assignment by accepting a name through the counter endpoint: /counter/:name

When entering this endpoint, the server should return the count of how many times this named endpoint has been visited.

For example

- Aaron enters /counter/Aaron 🠖 "Aaron was here 1 times"
- Aaron enters /counter/Aaron 🠖 "Aaron was here 2 times"
- Beatrice enters /counter/Beatrice 🠖 "Beatrice was here 1 times"
- Aaron enters /counter/Aaron 🠖 "Aaron was here 3 times"

# Extra: "url" library

With the "url" library, you can parse url fields (i.e., requests) the client sends to your server.

```ts
import  url from "url";
// URL module usage example

const addressString = 'http://localhost:5000/default.html?year=2017&month=february'; // Define the address
const address = url.parse(addressString, true); // Parse through the address with url.parse() function.
console.log(address.host); // returns 'localhost:5000'
console.log(address.pathname); // returns '/default.html'
console.log(address.search); // returns '?year=2017&month=february'
const query = address.query; // returns an object: { year: 2017, month: 'february' }
console_ .log(query.month); // returns 'february
```

# Express Middleware

# Middlewares

Generally speaking, middleware is an application that does something in between some other applications. 

There are tons of middleware libraries available for Express.

What this means in Express: with a middleware, you can manipulate the client request and server response in the client-server communication.

Middleware functions are functions that have access to the request object (`req`), the response object (`res`), and the `next` function in the application's request-response cycle. When the `next` function is invoked, the next middleware function is executed.

---

Middleware can be defined as such
```ts
const authCheck = (req: Request, res: Response, next: NextFunction) => {
  if (req.headers.Authentication === undefined) {    // Read the request object
    res.status(401).send('Missing Authentication Header')    // Modify the response object
    return
  }

  next()    // Execute the next middleware
}
```

In Express, .use() method initializes a middleware (argument 2) in a given path (argument 1).

In the middleware, the next() function will jump to the next middleware or endpoint.
```ts
server.use('/customers', authCheck)
server.get('/customers', (req, res) => {
// we know that Authentication header is present
...
```

# Exercise 5: Logger Middleware
<!--_class: "exercise invert" -->

Create a new project called Student Registry. We will be developing this program in stages during this lecture. For now it should have a single GET endpoint `/students` that returns an empty list.

Create a logger middleware function that is used in all the project's endpoints. The middleware should log

1) the time the request was made
2) the method of the request
3) the url of the endpoint



# Middleware - Unknown endpoint (AKA 404)

As you might have noticed, all websites have some kind of a default functionality for unknown endpoints.

This can be handled with a middleware.

Unknown endpoint has to be taken into use after the actual endpoints!
```ts
const unknownEndpoint = (_req: Request, res: Response) => {
  res.status(404).send({error: 'Page not found'})
}

app.use(unknownEndpoint)
```



# Middleware - Error handler

An error handler is often needed to handle different kind of user or application errors.

We can use middleware to enable error handling for our application.

Has to be the last middleware to be taken into use!
```ts
const errorHandler = (error: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(error.message)
  if (error.name === 'MyCustomError') {
    res.status(400).send({error: 'My Custom Error Message'})
  }

  next(error)
}

```



# Middleware - Req & Res

A need to manually alter req & res is quite common. To avoid bloated code, the handling can be easily done with proper middlewares.

Setting a header in middleware:
```ts
app.use((req: Request, res: Response, next: Next) => {
  res.setHeader('Content-Type', 'image/png');
  next();
});
```

---

Getting a header and storing its value. If the header is not present, return 403 with an 'Unauthorized' message:
```ts
app.use((req: Request, res: Response, next: NextFunction) => {
  const someIdNeededInApplication = req.header('Some-Custom-Header');
  if (!someIdNeededInApplication) {
    res.status(403).send('Unauthorized');
  }

  someIdFunctionality(someIdNeededInApplication);
  next();
});
```

# Exercise 6: 404 Not Found
<!--_class: "exercise invert" -->

Add a middleware function that sends a response with status code 404 and an appropriate error message, if user tries to use an endpoint that does not exist.

If you have not already done so, move all your middleware to a separate file, to keep your program clean and readable.



# Express.js: Creating a REST API

# REST HTTP requests

We have gone through what a GET request is. Let's briefly go through what other types of requests are necessary for you.

- GET `/products/:id` → Returns a single product identified by its id
- GET `/products` →Returns all the products
- POST `/products` → Creates a new product
- DELETE `/products/:id` → Removes a single product identified by its id
- PUT `/products/:id` → Updates an existing product

Basic RESTFUL functionality is often referenced as a CRUD (Create, Read, Update, Delete).



# Handling request content

To properly handle some requests, e.g., POST requests, in Express, a body-parser middleware must be taken into use. It's as simple as:
```ts
const server = express();
server.use(express.json());
```

You might encounter a separate body-parser being used. This is no longer necessary, as Express 4.16+ includes a body parser middleware built-in.

---

`express.urlencoded()`  allows us to parse url-encoded forms by attaching the data to the request body.
```ts
const app = express();
app.use(express.json());
app.use(express.urlencoded({extended: false}));
```

The "extended" parameter's false value only says that we are not dealing with any complicated objects (objects with sub objects, etc).



# Basic POST request

```ts
const app = express();
app.use(express.json());

app.get("/", (req: Request, res: Response) => {
  console.log("GET request init!");
  res.sendFile(__dirname + "/index.html");
});

app.post("/", (rreq: Request, res: Response) => {
  console.log("POST request init!");
  console.log(req.body);
  res.redirect("/");
});
```



# Request Body

GET methods should not in general have body parameters. The GET method should only be used to get data, the request should not convey data. 

Despite this, most modern implementations can send and receive request bodies in all request types.

The requests that should not include body are CONNECT, GET, HEAD, OPTIONS and TRACE.



# Request Body Types

Request body is parsed by the express body parser, and therefore it can contain any basic JavaScript data types. Notice that this differs from the request parameters and queries.

This also means that you do not know what the data type of any given body parameter will be. It might be necessary to check this in some cases.



# TypeScript Body Casting

If you want to enforce type safety, the solution is to validate the user input before using it. 

This is usually a good idea even if not using TypeScript, since it is important to know what type all the variables are in your code.

---

```ts
interface Body {
  name: string;
  age: number;
}

const validate = (req: Request, res: Response, next: NextFunction ) => {
  const { age, name } = req.body;
  if (typeof(age) !== 'number' || typeof(name) !== 'string') {
    return res.status(400).send('Missing or invalid parameters');
  }
  next();
}

server.post('/', validate, (req: Request, res: Response) => {
  const body: Body = req.body; // this has been validated
  console.log(body);
  res.send(body.name.toUpperCase());
});
```



# Exercise 7: Body Logging
<!--_class: "exercise invert" -->

Enable body parsing in your application.

Modify your logger middleware so that in addition to existing functionality, it also logs the request body if it exists.



# Exercise 8: POST Requests
<!--_class: "exercise invert" -->

Add two more endpoints to your app:

POST `/student` should expect the request body to include student *id*, *name* and *email*. If some of these parameters are missing, the endpoint should return a response with status code 400 and an error message. The endpoint should store the student information in an array called **students**. The endpoint should return an empty response with status code 201.

GET `/student/:id` should return the information of a single student, identified by the *id* request parameter. The response should be in JSON format. If there is no such student, the endpoint should return 404.

Modify the GET `/students` endpoint to return the list of all student *ids*, without names or emails.



# Exercise 9: PUT and DELETE
<!--_class: "exercise invert" -->

Add two more endpoints to your app:

PUT `/student/:id` should expect the request body to include student *name* or *email*. If both are missing, the endpoint should return a response with status code 400 and an error message. The endpoint should update an existing student identified by the request parameter *id*.

DELETE `/student/:id` should remove a student identified by the request parameter *id*.

Both endpoints should return 404 if there is no student matching the request parameter *id*. On success both endpoint should return an empty response with status code 204.