DEV Community

Tom
Tom

Posted on

Normalize Parcel Dimensions Before They Reach a Shipping Adapter

A dimension of 21.5 is incomplete input for a shipping adapter until its unit is known. Sending centimeters to a field interpreted as millimeters changes the parcel description even though the numeric value passes a basic positive-number check.

Carry the unit with the measurement at intake, then convert at a defined boundary. The example below uses millimeters as its internal representation. It deliberately accepts only dimensions that convert to positive whole millimeters; another system may need a different precision contract.

I work with YX Fulfillment. This is an adapter validation example, not a description of its production software or any carrier's rating formula. Physical parcel measurement and the receiving API's field contract still need to be checked for the actual shipping arrangement.

Keep the unit explicit in the input

The SI length reference from NIST describes the meter and related units. Here, centimeters convert to millimeters by a factor of ten. The converter accepts only the two unit codes declared in the example contract.

JavaScript's Number.isSafeInteger checks the converted value's integer representation and safe range. It does not establish measurement accuracy or choose a business rounding rule.

Save this as parcel-dimensions.cjs and run node parcel-dimensions.cjs. The assertions use Node's built-in strict assertion module.

const assert = require('node:assert/strict');
const factors = new Map([['mm', 1], ['cm', 10]]);

function toMillimeters(measurement) {
  if (!measurement || typeof measurement !== 'object' ||
      Array.isArray(measurement)) {
    throw new TypeError('Expected a measurement object');
  }
  const { value, unit } = measurement;
  const factor = factors.get(unit);
  if (typeof value !== 'number' || !Number.isFinite(value) ||
      value <= 0 || factor === undefined) {
    throw new TypeError('Expected a positive number with unit mm or cm');
  }
  const millimeters = value * factor;
  if (!Number.isSafeInteger(millimeters)) {
    throw new RangeError('Whole millimeters in the safe range required');
  }
  return millimeters;
}

function normalizeParcel(parcel) {
  return Object.fromEntries(['length', 'width', 'height'].map(axis =>
    [axis + 'Mm', toMillimeters(parcel[axis])]
  ));
}

const fromCm = normalizeParcel({
  length: { value: 21.5, unit: 'cm' },
  width: { value: 15, unit: 'cm' },
  height: { value: 4, unit: 'cm' }
});
const fromMm = normalizeParcel({
  length: { value: 215, unit: 'mm' },
  width: { value: 150, unit: 'mm' },
  height: { value: 40, unit: 'mm' }
});
assert.deepEqual(fromCm, { lengthMm: 215, widthMm: 150, heightMm: 40 });
assert.deepEqual(fromCm, fromMm);
assert.equal(toMillimeters({ value: 0.6, unit: 'cm' }), 6);
for (const bad of [
  { value: '21.5', unit: 'cm' },
  { value: 21.5 },
  { value: 215, unit: 'g' },
  { value: 0, unit: 'mm' },
  { value: -1, unit: 'mm' },
  { value: Infinity, unit: 'mm' },
  { value: NaN, unit: 'mm' },
  { value: 2.54, unit: 'cm' },
  { value: Number.MAX_SAFE_INTEGER + 1, unit: 'mm' }
]) assert.throws(() => toMillimeters(bad));
assert.throws(() => normalizeParcel({
  length: { value: 215, unit: 'mm' },
  width: { value: 150, unit: 'mm' }
}), TypeError);
console.log('All parcel-dimension checks passed');
Enter fullscreen mode Exit fullscreen mode

The equivalent-parcel assertion is the main business check: the same physical dimensions expressed in two accepted units produce the same internal values. The rejection cases cover missing units, a mass unit in a length field, nonnumeric input, invalid magnitudes and precision outside this example's contract.

The 2.54 cm case is rejected because it converts to 25.4 mm. This converter does not silently round it. Choose any required rounding rule from the destination interface's actual contract, document where it happens and test boundary values around that rule.

Convert again only where the destination requires it

Use names such as lengthMm inside the adapter so that the representation remains visible. If a destination expects another unit, perform that mapping explicitly at its request boundary. Verify the outbound request with a known measured parcel; do not infer the expected unit from a field named only length.

This example handles dimensions only. It does not validate weight, choose package orientation, calculate dimensional weight, determine a charge or impose a carrier's maximum size. Those are separate inputs and rules for the selected service.

For an integration acceptance check, compare the measured parcel, the normalized internal dimensions and the actual outbound request side by side. That catches a unit mismatch before a valid-looking number becomes an incorrect shipment description.

A mailing carton beside a metal ruler and a laptop

Checking the measurement behind a software field. Illustrative scene.

Top comments (0)