Course Banner

Account: Process Registration

Introduction

With the registration form built, it is time to implement it to add the account data to the database. This is only the first step. You'll revisit this process again in order to add checks and validation of the data in the next unit. But, one step at a time.

Video Overview

The video provides a general overview of the activity, but does not contain the detail needed to complete each process. Watch the video to obtain a general idea, but follow the written steps to complete the activity. This is the Transcript of the video.

The Registration View

The registration form has two attributes that must be reviewed, added or altered, to make the form send the proper information to the server. Let's take a look at these attributes now.

  1. Find and open the registration view in the views > account folder.
  2. Locate the opening "<form>" tag. Alter the tag, if needed, to look like the code below:
    <form action="/account/register" method="post">
    
  3. An Explanation

    • action="/account/register" - the path to be processed by the route, to understand what is being requested.
    • method="post" - the manner in which the data is being sent to the server.
    [Note: if you are sharp-eyed, you will have noticed that the path is the same as the one used to request delivery of the view. However, the "post" method is different, and that is what will be used to differentiate between the two. If this is too subtle a difference, then feel free to change "register" to something different, but make sure it still carries the meaning of what is to be done - register a new account.]
  4. Ensure no warnings or errors are displayed by VSC.
  5. Save the file.

Enable the Registration Route

You will recall that the server.js file already has the initial element of the "account" route in place. After all, it should already be using the correct route file to deliver the login and registration views. So, we can bypass it and move directly to the accountRoute.js file to add the needed functionality.

  1. Open the accountRoute.js file.
  2. Add a new comment to the route and the route, as shown below, to the file, below the two existing routes.
    router.post('/register', utilities.handleErrors(accountController.registerAccount))
    
  3. By now, this code should begin to be familiar. The key difference is the use of the "post" method to look within the "post" object, instead of the "get" object. Be sure to use the same value in the route as you used in the "action" attribute of the registration form.
  4. Ensure no warnings or errors are displayed by VSC.
  5. Save the file.

Account Model

Before turning your attention to the account controller, we need a function that will allow the data to be written to the "account" table of the database. We'll build it first, then implement it in the controller.

  1. Create a new account-model.js file in the models folder.
  2. At the top of the file, require the database > index file and store it to a local "pool" variable. Refer to the inventory-model file, for what this code should look like.
  3. Create a few empty lines beneath the "require" statement.
  4. Create the new function, using the code shown below:
/* *****************************
*   Register new account
* *************************** */
async function registerAccount(account_firstname, account_lastname, account_email, account_password){
  try {
    const sql = "INSERT INTO account (account_firstname, account_lastname, account_email, account_password, account_type) VALUES ($1, $2, $3, $4, 'Client') RETURNING *"
    return await pool.query(sql, [account_firstname, account_lastname, account_email, account_password])
  } catch (error) {
    return error.message
  }
}
  1. An Explanation

    • Lines 1-3 - a comment introducing the function.
    • Lines 4-5 - opens the function, declares it to use "async", provides the function name, and lists the four parameters the function expects to receive.
    • Line 6 - opens a "try", part of a "try - catch" error handling block.
    • Lines 7-9 - declares a "sql" variable and the SQL query to write the data to the database. Note: placeholders are used - $# - as part of the "parameterized statement" syntax. Additionally, 'Client' is included in the SQL statement to indicate the "type" of account being registered. The "RETURNING *" clause indicates to the PostgrSQL server to return values based on the record that was inserted. It is a way to confirm that the insertion worked.
    • Lines 10-11 - returns the result of the query execution. Note the use of "await" to wait until the promise has been replaced with data, from the query executing the SQL statement and replacing the placeholders with the actual data in the variables.
    • Line 12 - closes the "try" block and opens the "catch" block. Accepts an "error" variable to store any error that is thrown should the "try" block fail.
    • Line 13 - sends back any error message that is found in the error object.
    • Line 14 - ends the "catch" block.
    • Line 15 - ends the function, opened on line 4.
  2. Create an export statement at the bottom of the file to make the function available for use elsewhere.
  3. Ensure no warnings or errors are displayed by VSC.
  4. Save the file.

Account Controller

With the route and model function in place, the account controller needs to be built to process the incoming data, cause the client to be stored into the database and return a view to the client's browser. Let's make some magic.

  1. Find and open the accountController file in the controllers folder.
  2. Add a "require" statement at the top of the file to bring the account model into scope.
  3. Add the following function below the existing functions, leaving at least one empty line between the old and new functions.
    /* ****************************************
    *  Process Registration
    * *************************************** */
    async function registerAccount(req, res) {
      let nav = await utilities.getNav()
      const { account_firstname, account_lastname, account_email, account_password } = req.body
    
      const regResult = await accountModel.registerAccount(
        account_firstname,
        account_lastname,
        account_email,
        account_password
      )
    
      if (regResult) {
        req.flash(
          "notice",
          `Congratulations, you\'re registered ${account_firstname}. Please log in.`
        )
        res.status(201).render("account/login", {
          title: "Login",
          nav,
        })
      } else {
        req.flash("notice", "Sorry, the registration failed.")
        res.status(501).render("account/register", {
          title: "Registration",
          nav,
        })
      }
    }
    
  4. An Explanation

    • Lines 1-3 - a multi-line comment introducing the function.
    • Line 4 - begins the function, declaring it as asynchronous, and passing in request, response as parameters.
    • Line 5 - retrieves and stores the navigation bar string for use in the view.
    • Lines 6-7 - collects and stores the values from the HTML form that are being sent from the browser in the body of the request object. We have an additional step to take to make this work, but we'll do it once this function is finished.
    • Line 8 - left intentionally blank.
    • Line 9 - calls the function, from the model, and uses the "await" keyword to indicate that a result should be returned and wait until it arrives. The result is stored in a local variable.
    • Lines 10 - 13 - the parameters being passed into the function.
    • Line 14 - ends the model function call.
    • Line 15 - left intentionally blank.
    • Line 16 - opens an "if" structure to determine if a result was received.
    • Lines 17-21 - sets a flash message to be displayed. Notice the use of ticks, not single quotes, and the JavaScript object literal to display the client's name in the message.
    • Line 22 -calls the render function to return the login view, along with an HTTP 201 status code for a successful insertion of data.
    • Lines 23-24 - data within the data object being sent to the view. Note that the use of the login view. This gives the client the opportunity to practice using the username and password that they just used in the registration process.
    • Line 25 - closes the data object and render method.
    • Line 26 - closes the "if" block and opens the "else" block.
    • Line 27 - sets the failure message if the insertion failed.
    • Line 28 - calls the render function, sends the route to trigger a return to the registration view and sends a HTTP 501 status code. In this instance, the 501 status should be interpreted as "not successful".
    • Lines 29-30 -The elements of the data object being sent to the view.
    • Line 31 - closes the data object and render method.
    • Line 32 - closes the "else" block.
    • Line 33 - closes the function begun on line 4.
  5. Be sure to add the function name to the exports code at the bottom of the controller.
  6. Ensure no warnings or errors are displayed by VSC.
  7. Save the file.

Data Trail

In the function that was just added, the data collection list and parameter list used variable names that are the same as the column names that exist in the "account" table of the database.

You may ask, "Why are you mentioning this again?" The answer is, "Repetition! By re-emphasizing the use of database column names as input name values, variable, and parameter names, it make it very clear what the data value should be as it moves through the application." It creates a clear trail, from beginning (in the form) to end (in the database table). I strongly encourage you to follow this approach - use the database column names for form input names, variables and parameters. This will help you keep the data clear as it moves through the processes of the application.

Server.js File

As mentioned in the explanation, in order to collect the values from the incoming request body, we need to make the application aware of that functionality. We'll add that to the server.js file so that it is available throughout the application.

  1. Find and open the server.js file in the project folder.
  2. Open a terminal pane, within VSC.
  3. In the terminal, install a new package - body-parser:
    pnpm add body-parser
    
  4. When the package has finished installing, close the terminal.
  5. In the server.js file, locate the "Require Statements" section and add the body parser to it:
    const bodyParser = require("body-parser")
    
  6. Scroll down to the middleware section. Type these two lines to the bottom of this section. Both of which will make the body-parser available to the application:
    app.use(bodyParser.json())
    app.use(bodyParser.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded
    
  7. An Explanation

    • Line 1 - tells the express application to use the body parser to work with JSON data (to be used later).
    • Line 2 - tells the express application to read and work with data sent via a URL as well as from a form, stored in the request object's body. The "extended: true" object is a configuration that allows rich objects and arrays to be parsed. The final part is an inline comment pertaining to the entire line.
  8. Ensure no warnings or errors are displayed by VSC.
  9. Save the file.

Test Time

With the code in place, let's see if we can successfully register a new client and save their data to the database.

  1. Ensure that all the file names are correct, particularly those that are being required by other files.
  2. Open a VSC terminal and start the development server - "pnpm run dev".
  3. Open a browser to "localhost:5500".
  4. Click the "My Account" link in the header.
  5. The view should be delivered.
  6. Find and click the link to deliver the registration view.
  7. Fill in the form with your own or fake information. Submit the form.
  8. If everything worked, the success message should appear in the "login" view.
  9. To check that a false positive result did not occur, open the "pgAdmin" tool and connect to the remote database server.
  10. Once connected, query the "account" table. The data from the registration should appear as the last record in the table.
  11. If everything worked, do a fist-bump with someone sitting nearby. If it didn't work, consult the terminal for error messages, talk with your learning team, the TA, or the professor. But it needs to be working prior to moving forward.

Conclusion

Successfully adding a client to the database is a great first step, but we left a lot of vulnerabilities in the process. In the next lesson, the goal is to take care of those vulnerabilities. But, don't move forward until you can write the data and be directed to the correct view afterwards.