← back to NationalPaperHangers
Coerce garbage numeric booking fields to null instead of 500ing
3fa1e627755eee28369cffc60219aec1d7c71e84 · 2026-05-18 17:38:33 -0700 · SteveStudio2
A booking POSTed directly with a non-numeric square_feet,
ceiling_height_ft, or roll_count_estimate ran parseFloat/parseInt → NaN
straight into an INTEGER/NUMERIC column, so the INSERT failed with
"invalid input syntax for type integer: NaN" and the customer got an
opaque 500. A numOrNull() helper now parses each field and yields null
when the value isn't a finite number; square_feet additionally falls
back to the measured-walls total. Verified: a booking with all three
fields set to junk now returns 201 with the columns stored as null.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 3fa1e627755eee28369cffc60219aec1d7c71e84
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date: Mon May 18 17:38:33 2026 -0700
Coerce garbage numeric booking fields to null instead of 500ing
A booking POSTed directly with a non-numeric square_feet,
ceiling_height_ft, or roll_count_estimate ran parseFloat/parseInt → NaN
straight into an INTEGER/NUMERIC column, so the INSERT failed with
"invalid input syntax for type integer: NaN" and the customer got an
opaque 500. A numOrNull() helper now parses each field and yields null
when the value isn't a finite number; square_feet additionally falls
back to the measured-walls total. Verified: a booking with all three
fields set to junk now returns 201 with the columns stored as null.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
routes/api.js | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/routes/api.js b/routes/api.js
index ce80bc5..d4cf608 100644
--- a/routes/api.js
+++ b/routes/api.js
@@ -14,6 +14,16 @@ const stripe = require('../lib/stripe');
const { requireInstaller } = require('../lib/auth');
const router = express.Router();
+// Parse a possibly-garbage form value to a finite number, or null. The
+// booking endpoint accepts a directly-posted body, so a non-numeric
+// square_feet / ceiling_height_ft / roll_count_estimate would otherwise
+// reach an INTEGER/NUMERIC column as NaN and 500 the INSERT.
+function numOrNull(v) {
+ if (v === undefined || v === null || v === '') return null;
+ const n = parseFloat(v);
+ return Number.isFinite(n) ? n : null;
+}
+
// =======================================================================
// PUBLIC API
// =======================================================================
@@ -182,8 +192,10 @@ router.post('/installers/:slug/book', async (req, res, next) => {
]);
const surfaceState = (f.surface_state && ALLOWED_SURFACE.has(f.surface_state)) ? f.surface_state : null;
const accessCon = (f.access_constraints && ALLOWED_ACCESS.has(f.access_constraints)) ? f.access_constraints : null;
- const ceilingHeightFt = f.ceiling_height_ft ? Math.max(6, Math.min(40, parseFloat(f.ceiling_height_ft))) : null;
- const rollCountEst = f.roll_count_estimate ? Math.max(0, parseInt(f.roll_count_estimate, 10)) : null;
+ const ceilRaw = numOrNull(f.ceiling_height_ft);
+ const ceilingHeightFt = ceilRaw === null ? null : Math.max(6, Math.min(40, ceilRaw));
+ const rollRaw = numOrNull(f.roll_count_estimate);
+ const rollCountEst = rollRaw === null ? null : Math.max(0, Math.round(rollRaw));
const brandSku = f.brand_sku ? String(f.brand_sku).slice(0, 200) : null;
// 5-question intake additions: who is ordering, has wallpaper been selected,
@@ -200,8 +212,9 @@ router.post('/installers/:slug/book', async (req, res, next) => {
// shape. When the customer measured walls but left square_feet blank,
// fall back to the captured total so the installer always has a number.
roomCaptures = roomCapturesLib.sanitize(f.room_captures);
- const squareFeet = f.square_feet
- ? parseInt(f.square_feet, 10)
+ const sqftRaw = numOrNull(f.square_feet);
+ const squareFeet = (sqftRaw !== null && sqftRaw >= 0)
+ ? Math.round(sqftRaw)
: roomCapturesLib.totalSqFt(roomCaptures);
const ins = await client.query(
← 515caa4 Stop clobbering the customer's hand-entered square footage
·
back to NationalPaperHangers
·
Validate availability time windows instead of silently break da5acf0 →