Getting Started With Express Part 5

2018-08-08

In the last part of this tutorial series, we created two routes to fetch and add books to our bare minimum application. We had already connected MongoDB database in Part 4 by defining a schema and creating a model to connect with the routes. In this part, we are going to work with HTML forms.

To see if everything is working, open an instance of your server side app using npm run devstart script and mongod instance. After that, visit URL http://localost:3001/books in your browser and you will get the following result.

ss1

What are HTML forms?

An HTML form is a group of fields or elements described as HTML tags on a web page that a user can interact with to add or update to our application. This mechanism allows us to collect user input data in various forms such as checkboxes, radio buttons, text input fields, user id/email, password, etc. Forms are considered a secure way of sharing data with the server. To submit a form from the client to server we make use of POST HTTP request.

A form in HTML is made of as a collection of HTML elements inside

...
tag. It contains at least one element of type="submit".

1<form action="/url" method="post">
2 <label for="name">Enter name: </label>
3 <input id="name" type="text" name="name" placeholder="Enter your name here">
4 <input type="submit" value="OK">
5</form>

Above example is a basic HTML form that accepts a user to enter their name and submit it by pressing the input button. The submit input will be displayed as a button. The form attributes define the HTTP method used to send the data which is POST and the destination of the data on the server, that is the URL.

Building an Add Book Form

Since we are using a template engine, Pug, in our application to display data from the server to client, we will be creating the form using it. There is no difference except the indentation of tags and elements when it comes to writing a form using Pug. In index.pug add the below code:

1extends layout
2
3block content
4 h1= title
5 p Welcome to #{title}
6
7 hr
8 h2 Add a book
9
10 form(method='POST' action='/books/add')
11 div.form-group
12 label(for='title') Title
13 br
14 input.form-control(type='text' id='title' placeholder='title' name='title')
15 br
16 div.form-group
17 label(for='author') Author Name
18 br
19 input.form-control(type='author' id='author' placeholder='author' name='author')
20 br
21 div.form-group
22 label(for='summary') Book Sumary
23 br
24 textarea.formControl(name="summary", cols="30", rows="2")
25 br
26 div.form-group
27 label(for='isbn') ISBN
28 br
29 input.form-control(type='isbn' id='isbn' placeholder='isbn' name='isbn')
30 button.btn.btn-primary(type='submit') Add

Here we are using four fields which we will be submitting in our route. Now add the values to each field like the below screenshot:

ss2

Since we already have setup our /books/add route in the books route controller, we will be prompted with a success message like in the below screenshot.

ss3

Our form is using the same techniques as for displaying information through our models by using our routes in a controller file. The route sends the input data to the controller which performs any action required to add data to the database. It might sound a bit complicated if you are a beginner but remember all this is happening in split seconds and this is a universal mechanism to handle forms. To verify that the data has been submitted in the databases, we can visit URL http://localhost:3001/books to do so.

Adding Protection using helmet

To make an Express server app secure, the most needed package we need is to install and use helmet. Helmet is a third party library that helps to protect your app from some well-known web vulnerabilities by setting HTTP headers appropriately. Helmet is actually just a collection of nine smaller middleware functions that set security-related HTTP headers. These middleware function include:

  • csp sets the Content-Security-Policy header to help prevent cross-site scripting attacks and other cross-site injections.
  • hidePoweredBy removes the X-Powered-By header. hpkp Adds Public Key Pinning headers to prevent man-in-the-middle attacks with forged certificates.
  • hsts sets Strict-Transport-Security header that enforces secure (HTTP over SSL/TLS) connections to the server.
  • ieNoOpen sets X-Download-Options for IE8+.
  • noCache sets Cache-Control and Pragma headers to disable client-side caching.
  • noSniff sets X-Content-Type-Options to prevent browsers from MIME-sniffing a response away from the declared content-type.
  • frameguard sets the X-Frame-Options header to provide clickjacking protection.
  • xssFilter sets X-XSS-Protection to enable the Cross-site scripting (XSS) filter in most recent web browsers.

To install helmet, run

1npm isntall -S helmet

To use it in our app, modify app.js like this:

1// ...
2
3var helmet = require('helmet')
4app.use(helmet())
5
6// ...

It's best to use Helmet early in your middleware stack so that its headers are sure to be set.

Input Validation

Our last piece of form handling comes to handling input fields. We need to validate that the correct or valid fields are being entered by the user and not some random data in our form. Validation checks that entered values are appropriate for each field (are in the right range, format, etc.) and that values have been supplied for all required fields.

We are going to use express-validator to validate our form's input fields. Install the dependency:

1npm install -S express-validator

To use the validator in our controllers we have to require the functions we want to use from the 'express-validator/check'. We will first use validationResult from express-validator. Edit the bookController.js file like this.

1const Book = require('../models/book.model.js')
2
3const { validationResult } = require('express-validator/check')
4
5exports.booksList = (req, res) => {
6 Book.find()
7 .then(data => {
8 res.send(data)
9 })
10 .catch(err => {
11 console.log(err)
12 res.status(400).send('unable to fetch books')
13 })
14}
15
16exports.getBookById = (req, res) => {
17 Book.findById({ _id: req.params.id })
18 .then(data => {
19 res.send(data)
20 })
21 .catch(err => {
22 console.log(err)
23 res.status(400).send('unable to fetch books')
24 })
25}
26
27exports.addBook = (req, res) => {
28 let newBook = new Book(req.body)
29 console.log(newBook)
30
31 const errors = validationResult(req)
32 if (!errors.isEmpty()) {
33 return res.status(422).json({ errors: errors.array() })
34 }
35
36 newBook
37 .save()
38 .then(data => {
39 res.send('Success: Data submitted')
40 })
41 .catch(err => {
42 console.log(err)
43 res.status(400).send('Error: unable to save data')
44 })
45}

validationResult is an object that takes the incoming request from the client. It then, check for errors coming from check method of express-validator which we are going to implement in a minute. If there are no errors, the if condition is bypassed and the incoming data object is saved to the database as before. If there is an error caught by the if condition, the data is not saved and it results in an error.

Now, let us implement the second piece of this process. Edit books.js in the route folder.

1const express = require('express')
2const router = express.Router()
3const { check } = require('express-validator/check')
4const bookController = require('../controllers/booksController.js')
5
6// GET ALL BOOKS titles
7router.get('/', bookController.booksList)
8
9// GET a BOOK by id
10router.get('/:id', bookController.getBookById)
11
12router.post(
13 '/add',
14 [check('title').isLength({ min: 1 })],
15 bookController.addBook
16)
17
18module.exports = router

In the above example, notice how we are implementing a check for the title field in our form. The check basically says that the title field must have a length of at least one character. Otherwise, the incoming request will no pass.

In the below screenshot, we have left the title field empty. The validationResult runs, it throws an error.

ss4

Originally published at javabeginnerstutorial.com

I'm Aman working as an independent fullstack developer with technologies such as Node.js, ReactJS, and React Native. I try to document and write tutorials to help JavaScript, Web and Mobile developers.