aboutsummaryrefslogtreecommitdiff
path: root/files/ms/learn/server-side/express_nodejs/forms/index.html
blob: 6e50f7b27acf28890f5027e41c71eb0ec1fdefb5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
---
title: 'Express Tutorial Part 6: Working with forms'
slug: Learn/Server-side/Express_Nodejs/forms
tags:
  - Beginner
  - CodingScripting
  - Express
  - Forms
  - HTML forms
  - Learn
  - NeedsTranslation
  - Node
  - TopicStub
  - server-side
translation_of: Learn/Server-side/Express_Nodejs/forms
---
<div>{{LearnSidebar}}</div>

<div>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs/deployment", "Learn/Server-side/Express_Nodejs")}}</div>

<p class="summary">In this tutorial we'll show you how to work with HTML Forms in Express, using Pug, and in particular how to write forms to create, update, and delete documents from the database.</p>

<table class="learn-box standard-table">
 <tbody>
  <tr>
   <th scope="row">Prerequisites:</th>
   <td>Complete all previous tutorial topics, including <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></td>
  </tr>
  <tr>
   <th scope="row">Objective:</th>
   <td>To understand how write forms to get data from users, and update the database with this data.</td>
  </tr>
 </tbody>
</table>

<h2 id="Overview">Overview</h2>

<p>An <a href="/en-US/docs/Web/Guide/HTML/Forms">HTML Form</a> is a group of one or more fields/widgets on a web page that can be used to collect information from users for submission to a server. Forms are a flexible mechanism for collecting user input because there are suitable form inputs available for entering many different types of data—text boxes, checkboxes, radio buttons, date pickers, etc. Forms are also a relatively secure way of sharing data with the server, as they allow us to send data in <code>POST</code> requests with cross-site request forgery protection.</p>

<p>Working with forms can be complicated! Developers need to write HTML for the form, validate and properly sanitize entered data on the server (and possibly also in the browser), repost the form with error messages to inform users of any invalid fields, handle the data when it has successfully been submitted, and finally respond to the user in some way to indicate success.</p>

<p>In this tutorial we're going to show you how the above operations may be performed in <em>Express</em>. Along the way we'll extend the <em>LocalLibrary</em> website to allow users to create, edit and delete items from the library.</p>

<div class="note">
<p><strong>Note:</strong> We haven't looked at how to restrict particular routes to authenticated or authorised users, so at this point any user will be able to make changes to the database.</p>
</div>

<h3 id="HTML_Forms">HTML Forms</h3>

<p>First a brief overview of <a href="/en-US/docs/Web/Guide/HTML/Forms">HTML Forms</a>. Consider a simple HTML form, with a single text field for entering the name of some "team", and its associated label:</p>

<p><img alt="Simple name field example in HTML form" src="https://mdn.mozillademos.org/files/14117/form_example_name_field.png" style="border-style: solid; border-width: 1px; display: block; height: 44px; margin: 0px auto; width: 399px;"></p>

<p>The form is defined in HTML as a collection of elements inside <code>&lt;form&gt;...&lt;/form&gt;</code> tags, containing at least one <code>input</code> element of <code>type="submit"</code>.</p>

<pre class="brush: html">&lt;form action="/team_name_url/" method="post"&gt;
    &lt;label for="team_name"&gt;Enter name: &lt;/label&gt;
    &lt;input id="team_name" type="text" name="name_field" value="Default name for team."&gt;
    &lt;input type="submit" value="OK"&gt;
&lt;/form&gt;</pre>

<p>While here we have included just one (text) field for entering the team name, a form <em>may</em> contain any number of other input elements and their associated labels. The field's <code>type</code> attribute defines what sort of widget will be displayed. The <code>name</code> and <code>id</code> of the field are used to identify the field in JavaScript/CSS/HTML, while <code>value</code> defines the initial value for the field when it is first displayed. The matching team label is specified using the <code style="font-style: normal; font-weight: normal;">label</code> tag (see "Enter name" above), with a <code style="font-style: normal; font-weight: normal;">for</code> field containing the <code style="font-style: normal; font-weight: normal;">id</code> value of the associated <code style="font-style: normal; font-weight: normal;">input</code>.</p>

<p>The <code>submit</code> input will be displayed as a button (by default)—this can be pressed by the user to upload the data contained by the other input elements to the server (in this case, just the <code>team_name</code>). The form attributes define the HTTP <code>method</code> used to send the data and the destination of the data on the server (<code>action</code>):</p>

<ul>
 <li><code>action</code>: The resource/URL where data is to be sent for processing when the form is submitted. If this is not set (or set to an empty string), then the form will be submitted back to the current page URL.</li>
 <li><code>method</code>: The HTTP method used to send the data: <code>POST</code> or <code>GET</code>.
  <ul>
   <li>The <code>POST</code> method should always be used if the data is going to result in a change to the server's database, because this can be made more resistant to cross-site forgery request attacks.</li>
   <li>The <code>GET</code> method should only be used for forms that don't change user data (e.g. a search form). It is recommended for when you want to be able to bookmark or share the URL.</li>
  </ul>
 </li>
</ul>

<h3 id="Form_handling_process">Form handling process</h3>

<p>Form handling uses all of the same techniques that we learned for displaying information about our models: the route sends our request to a controller function which performs any database actions required, including reading data from the models, then generates and returns an HTML page. What makes things more complicated is that the server also needs to be able to process data provided by the user, and redisplay the form with error information if there are any problems.</p>

<p>A process flowchart for processing form requests is shown below, starting with a request for a page containing a form (shown in green):<img alt="" src="https://mdn.mozillademos.org/files/14478/Web%20server%20form%20handling.png" style="height: 649px; width: 800px;"></p>

<p>As shown in the diagram above, the main things that form handling code needs to do are are:</p>

<ol>
 <li>Display the default form the first time it is requested by the user.
  <ul>
   <li>The form may contain blank fields (e.g. if you're creating a new record), or it may be pre-populated with initial values (e.g. if you are changing a record, or have useful default initial values).</li>
  </ul>
 </li>
 <li>Receive data submitted by the user, usually in an HTTP <code>POST</code> request.</li>
 <li>Validate and sanitize the data.</li>
 <li>If any data is invalid, re-display the form—this time with any user populated values and error messages for the problem fields.</li>
 <li>If all data is valid, perform required actions (e.g. save the data in the database, send a notification email, return the result of a search, upload a file, etc.)</li>
 <li>Once all actions are complete, redirect the user to another page.</li>
</ol>

<p>Often form handling code is implemented using a <code>GET</code> route for the initial display of the form and a <code>POST</code> route to the same path for handling validation and processing of form data. This is the approach that will be used in this tutorial!</p>

<p>Express itself doesn't provide any specific support for form handling operations, but it can use middleware to process <code>POST</code> and <code>GET</code> parameters from the form, and to validate/sanitize their values.</p>

<h3 id="Validation_and_sanitization">Validation and sanitization</h3>

<p>Before the data from a form is stored it must be validated and sanitized:</p>

<ul>
 <li>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.</li>
 <li>Sanitization removes/replaces characters in the data that might potentially be used to send malicious content to the server.</li>
</ul>

<p>For this tutorial we'll be using the popular <a href="https://www.npmjs.com/package/express-validator">express-validator</a> module to perform both validation and sanitization of our form data.</p>

<h4 id="Installation">Installation</h4>

<p>Install the module by running the following command in the root of the project.</p>

<pre class="brush: bash">npm install express-validator --save
</pre>

<h4 id="Using_express-validator">Using express-validator</h4>

<div class="note">
<p><strong>Note:</strong> The <a href="https://github.com/ctavan/express-validator#express-validator">express-validator</a> guide on  Github provides a good overview of API. We recommend you read that to get an idea of all its capabilities (including creating custom validators). Below we cover just a subset that is useful for the <em>LocalLibrary</em>.</p>
</div>

<p>To use the validator in our controllers we have to <em>require</em> the functions we want to use from the <strong>'express-validator/check</strong>' and <strong>'express-validator/filter</strong>' modules, as shown below:</p>

<pre class="brush: js">const { body,validationResult } = require('express-validator/check');
const { sanitizeBody } = require('express-validator/filter');
</pre>

<p>There are many functions available, allowing you to check and sanitize data from request parameters, body, headers, cookies, etc., or all of them at once. For this tutorial, we'll primarily be using <code>body</code><code>sanitizeBody</code>, and <code>validationResult</code> (as "required" above).</p>

<p>The functions are defined as below:</p>

<ul>
 <li><code><a href="https://github.com/ctavan/express-validator#bodyfields-message">body(fields[, message])</a></code>: Specifies a set of fields in the request body (a <code>POST</code> parameter) to validate along with an optional error message that can be displayed if it fails the tests. The validation criteria are daisy-chained to the <code>body()</code> method. For example, the first check below tests that the "name" field is not empty and sets an error message "Empty name" if it is not. The second test checks that the age field is a valid date, and using <code>optional()</code> to specify that null and empty strings will not fail validation.

  <pre class="brush: js">body('name', 'Empty name').isLength({ min: 1 }),
body('age', 'Invalid age').optional({ checkFalsy: true }).isISO8601(),
</pre>
  You can also daisy chain different validators, and add messages that are displayed if the preceding validators are true.

  <pre class="brush: js">body('name').isLength({ min: 1 }).trim().withMessage('Name empty.')
    .isAlpha().withMessage('Name must be alphabet letters.'),
</pre>

  <div class="note">
  <p><strong>Note:</strong> You can also add inline sanitizers like <code>trim()</code>, as shown above. However sanitizers applied here ONLY apply to the validation step. If you want the final output sanitized, you need to use a separate sanitizer method, as shown below.</p>
  </div>
 </li>
 <li><code><a href="https://github.com/ctavan/express-validator#sanitizebodyfields">sanitizeBody(fields)</a></code>: Specifies a body field to sanitize. The sanitization operations are then daisy-chained to this method. For example, the <code>escape()</code> sanitization operation below removes HTML characters from the name variable that might be used in JavaScript cross-site scripting attacks.
  <pre class="brush: js">sanitizeBody('name').trim().escape(),
sanitizeBody('date').toDate(),</pre>
 </li>
 <li><code><a href="https://github.com/ctavan/express-validator#validationresultreq">validationResult(req)</a></code>: Runs the validation, making errors available in the form of a <code>validation</code> result object. This is invoked in a separate callback, as shown below:
  <pre class="brush: js">(req, res, next) =&gt; {
    // Extract the validation errors from a request.
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
        // There are errors. Render form again with sanitized values/errors messages.
        // Error messages can be returned in an array using `errors.array()`.
        }
    else {
        // Data from form is valid.
    }
}</pre>
  We use the validation result's <code>isEmpty()</code> method to check if there were errors, and its <code>array()</code> method to get the set of error messages. See the <a href="https://github.com/ctavan/express-validator#validation-result-api">Validation Result API</a> for more information.</li>
</ul>

<p>The validation and sanitization chains are middleware that should be passed to the Express route handler (we do this indirectly, via the controller). When the middleware runs, each validator/sanitizer is run in the order specified.</p>

<p>We'll cover some real examples when we implement the <em>LocalLibrary</em> forms below.</p>

<h3 id="Form_design">Form design</h3>

<p>Many of the models in the library are related/dependent—for example, a <code>Book</code> <em>requires</em> an <code>Author</code>, and <em>may</em> also have one or more <code>Genres</code>. This raises the question of how we should handle the case where a user wishes to:</p>

<ul>
 <li>Create an object when its related objects do not yet exist (for example, a book where the author object hasn't been defined).</li>
 <li>Delete an object that is still being used by another object (so for example, deleting a <code>Genre</code> that is still being used by a <code>Book</code>).</li>
</ul>

<p>For this project we will simplify the implementation by stating that a form can only:</p>

<ul>
 <li>Create an object using objects that already exist (so users will have to create any required <code>Author</code> and <code>Genre</code> instances before attempting to create any <code>Book</code> objects).</li>
 <li>Delete an object if it is not referenced by other objects (so for example, you won't be able to delete a <code>Book</code> until all associated <code>BookInstance</code> objects have been deleted).</li>
</ul>

<div class="note">
<p><strong>Note:</strong> A more "robust" implementation might allow you to create the dependent objects when creating a new object, and delete any object at any time (for example, by deleting dependent objects, or by removing references to the deleted object from the database).</p>
</div>

<h3 id="Routes">Routes</h3>

<p>In order to implement our form handling code we will need two routes that have the same URL pattern. The first (<code>GET</code>) route is used to display a new empty form for creating the object. The second route (<code>POST</code>) is used for validating data entered by the user, and then saving the information and redirecting to the detail page (if the data is valid) or redisplaying the form with errors (if the data is invalid).</p>

<p>We have already created the routes for all our model's create pages in <strong>/routes/catalog.js</strong> (in a <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/routes">previous tutorial</a>). For example, the genre routes are shown below:</p>

<pre class="brush: js">// GET request for creating a Genre. NOTE This must come before route that displays Genre (uses id).
router.get('/genre/create', genre_controller.genre_create_get);

// POST request for creating Genre.
router.post('/genre/create', genre_controller.genre_create_post);
</pre>

<h2 id="Express_forms_subarticles">Express forms subarticles</h2>

<p>The following subarticles will take us through the process of adding the required forms to out example application. You need to read and work through each one in turn, before moving on to the next one.</p>

<ol>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_genre_form">Create Genre form</a> — Defining our page to create <code>Genre</code> objects.</li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_author_form">Create Author form</a> — Defining a page for creating <code>Author</code> objects.</li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_book_form">Create Book form</a> — Defining a page/form to create <code>Book</code> objects.</li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_BookInstance_form">Create BookInstance form</a> — Defining a page/form to create <code>BookInstance</code> objects.</li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Delete_author_form">Delete Author form</a> — Defining a page to delete <code>Author</code> objects.</li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Update_Book_form">Update Book form</a> — Defining  page to update <code>Book</code> objects.</li>
</ol>

<h2 id="Challenge_yourself">Challenge yourself</h2>

<p>Implement the delete pages for the <code>Book</code>, <code>BookInstance</code>, and <code>Genre</code> models, linking them from the associated detail pages in the same way as our <em>Author delete </em>page. The pages should follow the same design approach:</p>

<ul>
 <li>If there are references to the object from other objects, then these other objects should be displayed along with a note that this record can't be deleted until the listed objects have been deleted.</li>
 <li>If there are no other references to the object then the view should prompt to delete it. If the user presses the <strong>Delete</strong> button, the record should then be deleted.</li>
</ul>

<p>A few tips:</p>

<ul>
 <li>Deleting a <code>Genre</code> is just like deleting an <code>Author</code> as both objects are dependencies of <code>Book</code> (so in both cases you can delete the object only when the associated books are deleted).</li>
 <li>Deleting a <code>Book</code> is also similar, but you need to check that there are no associated <code>BookInstances</code>.</li>
 <li>Deleting a <code>BookInstance</code> is the easiest of all, because there are no dependent objects. In this case you can just find the associated record and delete it.</li>
</ul>

<p>Implement the update pages for the <code>BookInstance</code>, <code>Author</code>, and <code>Genre</code> models, linking them from the associated detail pages in the same way as our <em>Book update </em>page.</p>

<p>A few tips:</p>

<ul>
 <li>The <em>Book update page</em> we just implemented is the hardest! The same patterns can be used for the update pages for the other objects.</li>
 <li>The <code>Author</code> date of death and date of birth fields, and the <code>BookInstance</code> due_date field are the wrong format to input into the date input field on the form (it requires data in form "YYYY-MM-DD"). The easiest way to get around this is to define a new virtual property for the dates that formats the dates appropriately, and then use this field in the associated view templates.</li>
 <li>If you get stuck, there are examples of the update pages in <a href="https://github.com/mdn/express-locallibrary-tutorial">the example here</a>.</li>
</ul>

<h2 id="Summary">Summary</h2>

<p><em>Express</em>, node, and third party packages on NPM provide everything you need to add forms to your website. In this article you've learned how to create forms using <em>Pug</em>, validate and sanitize input using <em>express-validator</em>, and add, delete, and modify records in the database.</p>

<p>You should now understand how to add basic forms and form-handling code to your own node websites!</p>

<h2 id="See_also">See also</h2>

<ul>
 <li><a href="https://www.npmjs.com/package/express-validator">express-validator</a> (npm docs).</li>
</ul>

<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs/deployment", "Learn/Server-side/Express_Nodejs")}}</p>

<h2 id="In_this_module">In this module</h2>

<ul>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node (Express) development environment</a></li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li>
 <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li>
</ul>

<p> </p>