EJS Posting Form Data

Registered members can download the FREE Get Started App. This is the project I used to compose articles about setting up VS Code and developing Node with Express and the Embedded JavaScript (EJS) view engine.

When an HTML form is submitted using the POST method with the default Content-Type of application/x-www-form-urlencoded, the data is sent to the server as a URL-encoded string (e.g., name=John&email=john%40example.com). By default, Express cannot directly access this data.

The express.urlencoded() middleware processes this raw data, converts it into a usable JavaScript object containing key-value pairs, and populates it in the req.body object within your route handlers.

This series will focus on posting form data from the client to the server. Validation should notify the user of invalid input as soon as possible. Password requirements should be listed and indicate if the requirement has been satisfied.

I will use the Get Started App's contact us form for this series. I added a checkbox and a radio group for demonstration. The form is in a Bootstrap modal using Bootstrap classes.

Contact Us Modal

You need to enable the express.urlencoded middleware to accept and decode application/x-www-form-urlencoded data. The extended: true option is recommended and uses the qs library to parse the data. The default limit is 100 kb but can be set for larger data transfers.

server.js
app.use(express.urlencoded({ extended: true, limit: '1mb' })); 

The form is a container with expected elements and attributes. Set the form's method attribute to POST and the action attribute to the post endpoint or route. The input elements represent the data posted to the server as key-value pairs. The input's name attribute is the key for the value. The input's type attribute determines the html layout. Labels identify the input and assist screen readers. The for attribute of the label must exactly match the id of its corresponding input element. Each input for a radio group has the same name attribute with unique values.

contact.ejs
<form id="contactForm" action="/contact-us" method="POST">            
    <div class="mb-3">                
        <label for="email">Your Email:</label>
        <input class="form-control" type="email" id="email" name="email" maxlength="255" required>
    </div>            
    <div class="mb-3">
        <label for="subject">Subject:</label>
        <input class="form-control" type="text" id="subject" name="subject" maxlength="100" required>
    </div>            
    <div class="mb-3">
        <label for="message">Message:</label>
        <textarea class="form-control mb-1" id="message" name="message" rows="4" cols="50" maxlength="500" required></textarea>
        <span id="character-count">0 of 500</span>
    </div>
    <div class="mb-3 alert alert-primary p-2">
        <div class="form-check">
            <input class="form-check-input" type="checkbox" id="urgent" name="urgent" value="true">
            <label class="form-check-label" for="urgent">Urgent Issue</label>
        </div>
    </div>            
    <h6 class="">Scale:</h6>
    <div class="d-inline-flex alert alert-primary p-2">
        <div class="form-check form-check-inline mt-1">
            <input type="radio" value="1" class="form-check-input" id="Scale1" name="scale">
            <label class="form-check-label" for="Scale1">1</label>
        </div>
        <div class="form-check form-check-inline mt-1">
            <input type="radio" value="2" class="form-check-input" id="Scale2" name="scale">
            <label class="form-check-label" for="Scale2">2</label>
        </div>
        <div class="form-check form-check-inline mt-1">
            <input type="radio" value="3" class="form-check-input" id="Scale3" name="scale">
            <label class="form-check-label" for="Scale3">3</label>
        </div>
        <div class="form-check form-check-inline mt-1">
            <input type="radio" value="4" class="form-check-input" id="Scale4" name="scale">
            <label class="form-check-label" for="Scale4">4</label>
        </div>
        <div class="form-check form-check-inline mt-1">
            <input type="radio" value="5" class="form-check-input" id="Scale5" name="scale">
            <label class="form-check-label" for="Scale5">5</label>
        </div>
    </div>
    <div class="mb-3">
        <label for="verifyhuman">Human Verification:</label><br>
        <input class="form-control d-inline" type="number" style="width: 70px;" id="verifyhuman" name="verifyhuman" required><label class="ms-2">+ 5 = 7</label>
    </div>
    <button class="btn btn-primary" type="submit">Send Message</button>
</form>

The Get Started App implements routers and controllers. The root-router.mjs imports the RootController and routes the post to the PostContactUs function.

root-router.mjs
rootRouter.post("/contact-us", RootController.PostContactUs);

If the checkbox is not checked, the checkbox's name key is undefined. If the checkbox is checked, the key's value is equal to the checkbox's value attribute. You can evaluate the expected value for a Boolean type. If no radio input is selected for the radio group, the name key is undefined, otherwise the key is equal to the selected input value attribute.

root-controller.mjs
RootController.PostContactUs = async (req, res, next) => {
  try {
    const { email, subject, message, urgent, scale, verifyhuman } = req.body;
    const isUrgent = urgent === 'true';
    let scaleInt = 0;
    if (typeof scale !== 'undefined') {
      scaleInt = parseInt(scale);
      if (Number.isNaN(scaleInt)) throw new Error("Scale is not a number.");
    }

    // Server-side validation
    ...
    // Configure contact us mailOptions
    ...

    const senderResult = await EmailSender.send(mailOptions);
    if (senderResult != null && senderResult.success) {
      console.log("Email sent: ", senderResult.info.response);
      return res.status(201).redirect('/about?statusalert=Email sent. We will respond ASAP.');
    } else {
      let error = "Error: result is null.";
      if (senderResult != null) { error = "Error: " + senderResult.error; }
      console.error(error);
      return res.status(201).redirect('/about?statusalert=Error: There was a problem sending the email.');
    }
  } catch (err) {
    next(err);
  }
}
Created: 2/16/26