Skip to content

Form data is returned in as strings, not in correct TS types #6517

Open
@IgorArnaut

Description

@IgorArnaut

I have this complicated form and I send data from it.

<form action="/oglasi/postavka" method="post">
  <fieldset>
    <legend>Informacije o zgradi</legend>
    <div>
      <label for="address">Adresa:</label>
      <input type="text" name="building[address]" id="address" maxlength="255" required>
    </div>
    <div>
      <label for="constructedIn">Izgrađen:</label>
      <input type="number" name="building[constructedIn]" id="constructedIn" required>
    </div>
    <div>
      <label for="numOfFloors">Spratnost:</label>
      <input type="number" name="building[numOfFloors]" id="numOfFloors" required>
    </div>
    <div>
      <label for="hasParking">Parking:</label>
      <input type="checkbox" name="building[hasParking]" id="hasParking">
    </div>
    <div>
      <label for="hasGarage">Garaža:</label>
      <input type="checkbox" name="building[hasGarage]" id="hasGarage">
    </div>
    <div>
      <label for="hasElevator">Lift:</label>
      <input type="checkbox" name="building[hasElevator]" id="hasElevator">
    </div>
    <div>
      <label for="hasCctv">Video nadzor:</label>
      <input type="checkbox" name="building[hasCctv]" id="hasCctv">
    </div>
    <div>
      <label for="hasIntercom">Interfon:</label>
      <input type="checkbox" name="building[hasIntercom]" id="hasIntercom">
    </div>
  </fieldset>

  <fieldset>
    <legend>Informacije o stanu</legend>
    <div>
      <label for="location">Lokacija:</label>
      <input type="text" name="apartment[location]" id="location" maxlength="255" required>
    </div>
    <div>
      <label for="floor">Sprat:</label>
      <input type="number" name="apartment[floor]" id="floor" required>
    </div>
    <div>
      <label for="area">Površina:</label>
      <input type="number" name="apartment[area]" id="area" required>
    </div>
    <div>
      <label for="price">Cena:</label>
      <input type="number" name="apartment[price]" id="price" required>
    </div>
    <div>
      <label for="numOfRooms">Broj soba:</label>
      <input type="number" name="apartment[numOfRooms]" id="numOfRooms" required>
    </div>
    <div>
      <label for="state">Stanje:</label>
      <select name="apartment[state]" id="state">
        <option value="Izvorno">Izvorno</option>
        <option value="Novo">Novo</option>
        <option value="Renovirano">Renovirano</option>
        <option value="Lux">Lux</option>
      </select>
    </div>
    <div>
      <label for="heating">Grejanje:</label>
      <select name="apartment[heating]" id="heating">
        <option value="Gradsko">Gradsko</option>
        <option value="Etažno">Etažno</option>
        <option value="Podno">Podno</option>
        <option value="Struja">Struja</option>
        <option value="Gas">Gas</option>
        <option value="TA">TA</option>
      </select>
    </div>
    <div>
      <label for="equipment">Opremljenost:</label>
      <select name="apartment[equipment]" id="equipment">
        <option value="Namešten">Namešten</option>
        <option value="Polunamešten">Polunamešten</option>
        <option value="Prazan">Prazan</option>
      </select>
    </div>
    <div>
      <label for="items">Stvari:</label>
      <select name="apartment[items]" id="items" multiple>
        <% items.forEach(item => { %>
        <option value="<%- item.id %>"><%- item.name %></option>
        <% }) %>
      </select>
    </div>
  </fieldset>
  <fieldset>
    <legend>Uslovi zakupa</legend>
    <div>
      <label for="availableOn">Useljiv:</label>
      <input type="date" name="terms[availableOn]" id="availableOn" required>
    </div>
    <div>
      <label for="hasDeposit">Depozit:</label>
      <input type="checkbox" name="terms[hasDeposit]" id="hasDeposit">
    </div>
    <div>
      <label for="isForStudents">Za studente:</label>
      <input type="checkbox" name="terms[isForStudents]" id="isForStudents">
    </div>
    <div>
      <label for="isForWorkers">Za radnike:</label>
      <input type="checkbox" name="terms[isForWorkers]" id="isForWorkers">
    </div>
    <div>
      <label for="isSmokingAllowed">Dozvoljeno pušenje:</label>
      <input type="checkbox" name="terms[isSmokingAllowed]" id="isSmokingAllowed">
    </div>
    <div>
      <label for="arePetsAllowed">Dozvoljeni ljubimci:</label>
      <input type="checkbox" name="terms[arePetsAllowed]" id="arePetsAllowed">
    </div>
  </fieldset>
  <fieldset>
    <legend>Oglas</legend>
    <div>
      <label for="title">Naslov:</label>
      <input type="text" name="listing[title]" maxlength="255" required id="title">
    </div>
    <div>
      <label for="description">Opis:</label>
      <textarea name="listing[description]" cols="40" rows="10" required id="description"></textarea>
    </div>
  </fieldset>
  <input type="submit" value="<%= value %>">
</form>

The route that handles the POST of this form looks like this:

router.post("/postavka", async (req, res, next) => {
  const reqBody = req.body;
  const { building, apartment, terms, listings } = reqBody;

  for (let field in building) {
    console.log(field);
  }
  console.log(reqBody);
  res.status(200).json(reqBody);
});

The resulting data looks like this:

{
  "building": {
    "address": "Alekse Santica 4, Novi Sad",
    "constructedIn": "2009",
    "numOfFloors": "1",
    "hasParking": "on",
    "hasCctv": "on",
    "hasIntercom": "on"
  },
  "apartment": {
    "location": "Grbavica",
    "floor": "0",
    "area": "30",
    "price": "30",
    "numOfRooms": "0",
    "state": "Izvorno",
    "heating": "Gradsko",
    "equipment": "Namešten",
    "items": [
      "1",
      "2",
      "16"
    ]
  },
  "terms": {
    "availableOn": "2025-01-01",
    "hasDeposit": "on",
    "isForStudents": "on"
  },
  "listing": {
    "title": "A",
    "description": "A."
  }
}

Everything is returned as string. Checkbox values should be boolean, even false if they are unchecked. Number input values should be number. Date input values should be Date. I need to pass this data into Prisma ORM functions.

I know that it's a default JS thing and that it should be manually handled, but it would be a waste of time to convert all of the values, especially when you have a complex form data such as this. There are libraries busboy, multiparty, formidable, multer, but from what I've seen, those packages are primarily used for FILE uploads. I have not seen examples of those packages being used for such forms. I've also seen examples that used only text inputs. I've tried formidable, and even this package returned form data in wrong types.

I made a form parser that converts data into correct types, but it doesn't show unchecked checkbox values as false:

export default (obj) => {
  for (let field in obj) {
    if (!isNaN(obj[field])) obj[field] = Number(obj[field]);
    if (obj[field] == "on") obj[field] = true;
    const regexp = RegExp("^d{4}-d{2}-d{2}$");
    if (regexp.test(obj[field])) obj[field] = new Date(obj[field]);
  }

  console.log(obj);
  return obj;
};

Environment information

Version:

"dependencies": {
    "@prisma/client": "^6.7.0",
    "body-parser": "^2.2.0",
    "debug": "~2.6.9",
    "ejs": "^3.1.10",
    "express": "^4.21.2",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1",
    "pug": "^3.0.3"
},
"devDependencies": {
    "@types/body-parser": "^1.19.5",
    "@types/debug": "^4.1.12",
    "@types/ejs": "^3.1.5",
    "@types/express": "^5.0.1",
    "@types/http-errors": "^2.0.4",
    "@types/morgan": "^1.9.9",
    "@types/node": "^22.15.3",
    "@types/pug": "^2.0.10",
    "prisma": "^6.7.0",
    "tsx": "^4.19.4",
    "typescript": "^5.8.3"
},

Platform:
Windows: Microsoft Windows NT 10.0.26100.0 x64

Node.js version:
v22.15.0

What steps will reproduce the bug?

  1. Create an Express project
  2. Create a page with a form
  3. Create get (to insert existing values into form, e.g. select input) and post routes for form
  4. Send form data to Express with POST
  5. Display form data as JSON
  6. It's all strings.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions