Skip to content

Exercises 6 : Express (15p)

Remember use JAMK/GitLab to return your exercises. Student need to test that exercise really work.

Exercise 01 - REST API [10p]

You will create a small REST API with Node.js and Express. This exercise will store all the users data with JSON, without any interactions to the real database. Later, you will learn to use the application in the background, e.g. a MongoDB database where changes are stored permanently.

You can use VS Code's REST client or Postman. Screenshots of the Postman application are visible in the instructions.

Project

Create a new Node based project, use/install Express and Nodemon.

Check project package.json file.

1
2
3
4
5
6
7
8
9
{
  //...
  "dependencies": {
    "express": "^4.18.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.20"
  }
}

Add a new starting script to package.json which uses Nodemon.

1
2
3
4
5
"scripts": {
  "start": "node index.js",
  "dev": "nodemon index.js",
  "test": "echo \"Error: no test specified\" && exit 1"
},

Start your project with Nodemon.

1
npm run dev

JSON data

Create a index.js and use Express.

1
2
3
4
5
6
7
const express = require('express') 
const app = express()
const port = 3000

app.listen(port, () => {
  console.log('Example app listening on port 3000')
})

Add sample user JSON data.

1
2
3
4
5
let users = 
[
  { 'id':'1', 'name':'Kirsi Kernel' },
  { 'id':'2', 'name':'Matti Mainio' }
]

Routes

Here is a simple chart of basic REST APIs you will make for your users application:

Resource GET read POST create PUT update DELETE
/users Returns a list of users Create a new user - -
/users/1 Returns a specific user - Updates a specific user Deletes a specific user

GET all the users (read)

Create a new API endpoint (route) to handle GET request and return all the users.

1
2
3
4
// get all users
app.get('/users', (request, response) => {
  response.json(users)
})

Test GET request to http://localhost:3000/users/ with a Postman or browser. You should see the returned JSON data.

Image 01

GET one user (read)

Create a new API endpoint (route) to handle single user with GET request.

1
2
3
4
5
6
7
8
// get one user
app.get('/users/:id', (request, response) => {
  // note how you can do this in different ways!  
  // const id = request.params.id
  const { id } = request.params
  const user = users.find(user => user.id === id)
  response.json(user)
})

Now route id parameter is used as a our user id. Correct user will be find from the users collection and found user data will be returned to the caller.

Note

You will need to use : to add parameters to route path.

Test GET request to http://localhost:3000/users/1 with a Postman or browser. You should see the returned user JSON data.

!Image 02

Note

Server will return status code 200 and empty content, if you try to use a GET request parameter with id that doesn't exists in the users collection.

You should check that users exists and return correct data and for example 404 not found.

1
2
3
4
5
6
7
8
// get one user
app.get('/users/:id', (request, response) => {  
  const { id } = request.params
  const user = users.find(user => user.id === id)
  // check if user exists or return 404
  if (user) response.json(user)
  else response.status(404).end()
})

DELETE one user (delete)

One user can be deleted with using HTTP DELETE request. Request looks the same as above get one user, only a DELETE is used as a request.

1
2
3
4
5
6
7
// delete one user
app.delete('/users/:id', (request, response) => {
  const { id } = request.params
  users = users.filter(user => user.id !== id)
  // Just send "204 no content" status code back
  response.status(204).end()
})

First use GET request with Postman to get all the users JSON data from the server.

!Image 03

Use DELETE request to delete one of the users from server. See Status: 204 No Content in Postman.

!Image 04

Finally request all the users again to see changes.

!Image 05

PUT user data (update)

You can use PUT request to update user data in server side.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// update user data
app.put('/users/:id', (request, response) => {
  //const id = request.params.id
  const { id } = request.params
  // const name = request.query.name
  const { name } = request.query
  const user = users.find(user => user.id === id)
  if (user) {
    user.name = name
    response.status(200).end()
  } else {
    response.status(204).end()
  }
})

First use GET request with Postman to get all the users JSON data from the server (just for checking the users JSON data).

!Image 06

Use PUT request to send a new user data to server. Use Query Params to set a new user name and click Send button to send a new request to server. Use URI which contains some of your users id, for example: http://localhost:3000/users/1

Note

Query parameters are appended to the end of the request URL, following ? and listed in key value pairs, separated by & using the following syntax: ?id=1&type=new

Look how request URI will include your user name to query parameters.

!Image 07

Finally, request all the users again to see changes.

!Image 08

POST user data (create)

Use Postman to send POST request to server side. This time we will use JSON data, which will be included in request body.

  • Change request type to POST
  • Use http://localhost:3000/users/ route
  • Select body and raw to write use JSON data
  • Remember select JSON data type.

And write user name in JSON

1
2
3
{
  "name" : "Matt Markusson"
}

In a server side, data will be inside request.body object. You can use Express JSON parser to get it easily. This express.json() is a built-in middleware function in Express.

Now Express JSON parser will change the incoming JSON data (inside request) to JSON object (line 3) and it will available inside request.body object (line 8 - inside post route). User data will be send back to caller application with JSON (only testing purposes right now).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const express = require('express') 
const app = express()
app.use(express.json())
///...

// create user
app.post('/users/', (request, response) => {
  const user = request.body
  response.json(user)
})

Now send a POST request to your server with Postman and you should see the same JSON data returned from the server side.

!Image 9

Each user should have an unique id. Now databases are not helping us, so we need to generate a new id for the user. Let's loop through all the users and find the biggest id and increase it by one.

1
2
3
4
5
6
7
8
// create a new user
app.post('/users/', (request, response) => {
  const maxId = Math.max(...users.map(user => user.id), 0)
  const user = request.body
  user.id = (maxId+1).toString() 
  users = users.concat(user) 
  response.json(user)
})

Note

JavaScript map function will return all the id's in an array and ... moves array to integer values. So, now max function can find the biggest id value. New user will be added to users with JavaScript arrays concat function.

Read more about

Now send a new POST request to your server with Postman and you should see a new user JSON returned from the server side.

!Image 10

Send GET request to server side and check that you have all the old users and the new one in the server side.

!Image 11

Exercise 02 - Middleware [5p]

In this exercises you need to modify previous example so, that all incoming requests will be saved to text file. Use middleware function that work in Express. Middleware should work "between" the request coming from the client application and the response sent from the server.

For example, a middleware function can be inside a route handling a request, as shown below. The middleware function below (between the request and the return) does nothing but print the date information to the console (server) and then perform the actual return to the client.

Note that you should define a new callback function after the request and response objects, which will be called when the route programming is completed. The name next is defined for the function below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
app.get('/dogs', (request, response, next) => {
    // route level middleware
    console.log(new Date().toISOString())
    // call next function (line 7)
    next()
  },
  (request, response) => {
    console.log('response')
    response.status(200).end()
  }
)

So, own middleware functions can be added to each request. Try the above for a route (endpoint) you made earlier.

In this exercise, however, you should make a middleware function for the main level of the application (application level middleware). In other words, you should grab all the different requests (routes) coming to the server and save the information in a text file and then let the application forward the request to the corresponding route (endpoint). So, you shouldn't do this for each route separately (duplicate code), but use the main level of the application.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const express = require('express') 
const app = express()
const port = 3000

// application level middleware here

// route1
// route2
//...
// routeN

app.listen(port, () => {
  console.log('Example app listening on port 3000')
})

Create a new logger function and place it before the first route (see code above). Note that you should enable your own middleware with Express's use function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// create logger
const logger = (request, response, next) => {
  const date = new Date()
  const lDate = `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`
  const log = `${lDate}: ${request.method} ${request.url}\n`
  console.log(log)
  next()
}

// use own made logger middleware in express app
app.use(logger)

Now try sending different requests from Postman or Visual Studio Code's Rest Client to your server and see what kind of information is visible in the server's console.

GET-request example:

1
2
Example app listening on port 3000
14/09/2022 14:18:47: GET /users

Change your code so that the logging information is SAVED to a file.

Exercise is returned by pointing to the URL to the source codes of the program and to one example of the log file, which shows information about different requests.