Form data is returned in as strings, not in correct TS types #6600
-
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 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 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 informationVersion: "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: Node.js version: What steps will reproduce the bug?
|
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
Can you please check if you are adding express json middleware or not?
Complete structure below
|
Beta Was this translation helpful? Give feedback.
-
I use const app = express();
const __dirname = import.meta.dirname;
// view engine setup
app.set("views", join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(logger("dev"));
app.use(express.static(join(__dirname, "public")));
app.use("/", indexRouter);
app.use("/users", usersRouter);
app.use("/oglasi", listingsRouter);
// ...
export default app; Your suggestion didn't work. |
Beta Was this translation helpful? Give feedback.
-
The thing is that I don't want this data: {
"building": {
"address": "Alekse Šantića 4",
"constructedIn": "2009",
"numOfFloors": "0",
"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",
"4",
"6",
"16"
]
},
"terms": {
"availableOn": "2025-05-01",
"hasDeposit": "on",
"isForStudents": "on"
},
"listing": {
"title": "Naslov",
"description": "Opis."
}
} But this: {
"building": {
"address": "Alekse Šantića 4",
"constructedIn": 2009,
"numOfFloors": 0,
"hasParking": true,
"hasGarage": false,
"hasElevator": false,
"hasCctv": true,
"hasIntercom": true
},
"apartment": {
"location": "Grbavica",
"floor": 0,
"area": 30,
"price": 30,
"numOfRooms": 0,
"state": "Izvorno",
"heating": "Gradsko",
"equipment": "Namešten",
"items": [
"1",
"2",
"4",
"6",
"16"
]
},
"terms": {
"availableOn": "2025-05-01",
"hasDeposit": true,
"isForStudents": true,
"isForWorkers": false,
"isSmokingAllowed": false,
"arePetsAllowed": false
},
"listing": {
"title": "Naslov",
"description": "Opis."
}
} So I can use the form data object in the route with Prisma: router.post("/postavka", async (req, res, next) => {
console.log(req.body);
let {
building: buildingData,
apartment: apartmentData,
terms: temrsData,
listing: listingData,
} = req.body;
buildingData = trueParse(buildingData);
apartmentData = trueParse(apartmentData);
temrsData = trueParse(temrsData);
console.log(buildingData);
let building = await prisma.building.findFirst({ where: buildingData });
if (!building)
building = await prisma.building.create({ data: buildingData });
res.status(200).json({
building: buildingData,
apartment: apartmentData,
terms: temrsData,
listing: listingData,
});
}); |
Beta Was this translation helpful? Give feedback.
-
Unless you have special client-side JS code to change it, HTML
Neither of those encodings preserves information about data type (number, string, bool, Date), so effectively everything is a string. One exception is Checkboxes and radio buttons that are not checked are not sent. Checked ones are sent with value being their This has nothing to do with Express. It's just how HTML forms work. You have a couple options to solve your problems:
|
Beta Was this translation helpful? Give feedback.
Unless you have special client-side JS code to change it, HTML
<form>
s can be sent as:application/x-www-form-urlencoded
(default),multipart/form-data
, ortext/plain
.Neither of those encodings preserves information about data type (number, string, bool, Date), so effectively everything is a string. One exception is
multipart/form-data
, which sends a bit more information about files.Checkboxes and radio buttons that are not checked are not sent. Checked ones are sent with value being their
value
attribute if it is specified or the string"on"
.This has nothing to do with Express. It's just how HTML forms work.
You have a couple options to solve your problems: